diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80eddcc --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# On branch master +# Untracked files: +# (use "git add ..." to include in what will be committed) +# +# .metadata/ +# gitignore +*~ +*.o +*.a +*.so +*.obj +*.lib +*.dll +*.suo +*.ilk +*.exp +*.sdf +*.pdb +*.opensdf +*.vcxproj.user +Debug/ +Release/ +lua +luac +node-lua +node-lua.exe \ No newline at end of file diff --git a/Makefile b/Makefile index f27fc49..b389575 100644 --- a/Makefile +++ b/Makefile @@ -10,11 +10,10 @@ ROOT_PATH = $(shell pwd) define MAKE_PLATFORM ##main essential builds + ##disable dtrace cd deps/lua && make $(2); cd $(ROOT_PATH) - cd deps/uv && make PLATFORM=$(1); rm -f libuv.so libuv.dylib; cd $(ROOT_PATH) - cd src && make PLATFORM=$(1) RELEASE=$(3); cd $(ROOT_PATH) - ##not essential builds - cd clib/mime && make PLATFORM=$(1) RELEASE=$(3); cd $(ROOT_PATH) + cd deps/uv && make PLATFORM=$(1) HAVE_DTRACE=0; rm -f libuv.so libuv.dylib; cd $(ROOT_PATH) + cd src && make PLATFORM=$(1) HAVE_DTRACE=0 RELEASE=$(3); cd $(ROOT_PATH) endef ifeq (darwin,$(PLATFORM)) @@ -34,8 +33,6 @@ clean: cd deps/lua && make clean; cd $(ROOT_PATH) cd deps/uv && make clean; cd $(ROOT_PATH) cd src && make clean; cd $(ROOT_PATH) - ##not essential builds - cd clib/mime && make clean; cd $(ROOT_PATH) # Where to install. The installation starts in the src and doc directories, # so take care if INSTALL_TOP is not an absolute path. @@ -51,6 +48,4 @@ install: cp -f src/node-lua ./node-lua cp -f src/node-lua /usr/local/bin/node-lua cp deps/lua/libnlua.so ./libnlua.so - cp deps/lua/libnlua.so /usr/local/lib/libnlua.so - ##not essential install - cp clib/mime/mime.so clib/mime.so \ No newline at end of file + cp deps/lua/libnlua.so /usr/local/lib/libnlua.so \ No newline at end of file diff --git a/README.md b/README.md index d8ec25b..b915022 100644 --- a/README.md +++ b/README.md @@ -8,82 +8,261 @@ 5. A context starts with a lua file as the entry. The process will exit automaticly when all contexts ends or terminates and a context will exit automaticly when all its running and suspending sync and async remote procedure call ends or terminates. 6. A optimized task scheduling is used with a thread based context queue which reduces the thread race condition and work-stealing algorithm is used in the task scheduling. 7. A more friendly tcp api is embeded in this engine with sync and async implementation which is much more convenient to build a tcp server. +8. A shared buffer api is much more efficient for sending a broadcast packet. ## build & install -For windows, just open node-lua.sln and build the whole solution. For linux or other unix-like system -``` -git clone https://github.com/socoding/node-lua.git -cd node-lua -make && make install -make release && make install -``` - -## usefull api -### tcp api -1. result, listen_socket = tcp.listen(addr, port[, backlog, [listen_callback(result, listen_socket, addr, port[, backlog])]]) - --listen_callback is a once callback, blocking if callback is nil. +For windows, just open node-lua.sln and build the whole solution. For linux or other unix-like system where centos, macos, freebsd and ubuntu has been successfully supported and tested, run the flowing shell scripts: + + git clone https://github.com/socoding/node-lua.git + cd node-lua + make [release] #add release to build for release versions + make install +## contact +See https://github.com/socoding/node-lua/wiki for detailed node-lua description. +Contact with **Email: xdczju@sina.com** or **QQ: 443231647** + +## usefull api +### context api +1. **context.self** + *`--the running context id.`* + +2. **context.name** + *`--the running context full name with all args.`* + +3. **context.parent** + *`--the running context parent id.`* + +4. **context.winos** + *`--whether the running system is windows.`* + +5. **context.ncpu** + *`--number of cpu.`* + +6. handle = **context.create**(file_name[, arg1[, ...]]) + *`--create a new context with file_name as the context entry. arg1, arg2, ... will be the argument for the context.`* + +7. handle = **context.destroy**([handle[, message]]) + *`--destroy a context specified by handle with a string message. You'll kill the context itself if handle is nil and message is a optional argument.`* + +8. result, error = **context.send**(handle, data1[, ...]) + *`--send data1, data2, ... directy to context specified by handle noblocking.`* + +9. result, query_data1, ... = **context.query**(handle, data1[, ... [, query_callback(result, query_data1[, ...])]]) + *`--query context specified by handle with data1, data2, ... and query_data1, query_data2, ... is the queried datas.`* + *`--query_callback is a once callback, blocking if query_callback is nil.`* + +10. result, query_data1, ... = **context.timed_query**(handle, timeout, data1[, ... [, query_callback(result, query_data1[, ...])]]) + *`--query context specified by handle with data1, data2, ... in timeout seconds`* + *`--query_callback is a once callback, blocking if query_callback is nil.`* -2. result, listen_socket = tcp.listens(sock_name[, listen_callback(result, listen_socket, sock_name)]) - --listen_callback is a once callback, blocking if callback is nil. +11. result, error = **context.reply**(handle, session, data1[, ...]) + *`--reply session(received by context.recv) context specified by handle with data1, data2, ...`* -3. result, listen_socket = tcp.listen6(addr, port[, backlog, [listen_callback(result, listen_socket, addr, port[, backlog])]]) - --listen_callback is a once callback, blocking if callback is nil. +12. result, recv_handle, session, recv_data1, ... = **context.recv**(handle[, timeout]) + *`--receive data from context specified by handle(receive data from all contexts if handle equals 0).`* + *`--recv_handle specifies the source context id. It's a query action if session >= 0, where you'd better reply this query action.`* -4. result, accept_socket = tcp.accept(listen_socket[, timeout]) - --accept_callback is a continues callback, blocking if callback is nil. +13. result, recv_handle, session, recv_data1, ... = **context.recv**(handle[, recv_callback(result, recv_handle, session, recv_data1, ...)]) + *`--receive data from context specified by handle(receive data from all contexts if handle equals 0).`* + *`--recv_handle specifies the source context id. It's a query action if session >= 0, where you'd better reply this query action.`* + *`--recv_callback is a continues callback, blocking if recv_callback is nil.`* + +14. result, error = **context.wait**(handle[, timeout[, callback(result, error, handle[, timeout])]]) + *`--wait context to quit or to be destroyed in blocking or nonblocking mode.`* + *`--callback is a once callback, blocking if callback is nil.`* + +15. result, error = **context.log**(...) + *`--almost equivalent to print except the output won't be disordered(node-lua runs in a muti-thread mode).`* + +16. error = **context.strerror**(errno) + *`--convert error number to error string. Error number is always the next argument after result in most apis.`* + +17. thread = **context.thread**() + *`--return the running thread index.`* + +18. **context.run**(func[, ...]) + *`--run the func(...) at next tick(in other words next processing period).`* -5. result, accept_socket = tcp.accept(listen_socket[, accept_callback(result, accept_socket, listen_socket)]) - --accept_callback is a continues callback, blocking if callback is nil. +### tcp api + +1. result, listen_socket = **tcp.listen**(addr, port[, reuse, [listen_callback(result, listen_socket, addr, port[, reuse])]]) + *`--listen on a ipv4 address.`* + *`--listen_callback is a once callback, blocking if listen_callback is nil.`* + *`--reuse option allows you to listen on the same specified port.`* + *`--If you are listening on the same port among several sockets at unix system, you will accept the connecting socket in round-robin sequence among all the listening sockets.`* + *`--If you are listening on the same port among several sockets at windows system, you will most likely accept the connecting socket at the first listening socket!`* + +2. result, listen_socket = **tcp.listens**(sock_name[, listen_callback(result, listen_socket, sock_name)]) + *`--listen on a windows named pipe or unix domain socket.`* + *`--listen_callback is a once callback, blocking if listen_callback is nil.`* + +3. result, listen_socket = **tcp.listen6**(addr, port[, reuse, [listen_callback(result, listen_socket, addr, port[, reuse])]]) + *`--listen on a ipv6 address.`* + *`--listen_callback is a once callback, blocking if listen_callback is nil.`* + *`--reuse option allows you to listen on the same specified port.`* + *`--If you are listening on the same port among several sockets at unix system, you will accept the connecting socket in round-robin sequence among all the listening sockets.`* + *`--If you are listening on the same port among several sockets at windows system, you will most likely accept the connecting socket at the first listening socket!`* -6. result, connect_socket = tcp.connect(host_addr, host_port[, timeout [, connect_callback(result, connect_socket, host_addr, host_port[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. +4. result, accept_socket = **tcp.accept**(listen_socket[, timeout]) + *`--accept on a listen socket in blocking mode, timeout is activated if timeout is set.`* + +5. result, accept_socket = **tcp.accept**(listen_socket[, accept_callback(result, accept_socket, listen_socket)]) + *`--accept_callback is a continues callback, blocking if accept_callback is nil.`* -7. result, connect_socket = tcp.connects(sock_name[, timeout [, connect_callback(result, connect_socket, host_addr, host_port[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. +6. result, connect_socket = **tcp.connect**(host_addr, host_port[, timeout [, connect_callback(result, connect_socket, host_addr, host_port[, timeout])]]) + *`--connect_callback is a once callback, blocking if connect_callback is nil.`* -8. result, connect_socket = tcp.connect6(host_addr, host_port[, timeout [, connect_callback(result, connect_socket, host_addr, host_port[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. +7. result, connect_socket = **tcp.connects**(sock_name[, timeout [, connect_callback(result, connect_socket, host_addr, host_port[, timeout])]]) + *`--connects to a windows named pipe or unix domain socket.`* + *`--connect_callback is a once callback, blocking if connect_callback is nil.`* -9. result, buffer = tcp.read(socket[, timeout]) - --recv_callback is a continues callback, blocking if callback is nil. +8. result, connect_socket = **tcp.connect6**(host_addr, host_port[, timeout [, connect_callback(result, connect_socket, host_addr, host_port[, timeout])]]) + *`--connect_callback is a once callback, blocking if connect_callback is nil.`* -10. result, buffer = tcp.read(socket[, recv_callback(result, buffer, socket)]) - --recv_callback is a continues callback, blocking if callback is nil. +9. result, buffer = **tcp.read**(socket[, timeout]) -11. result, error = tcp.write(socket, buffer_or_lstring[, send_callback(result, error, socket, buffer_or_lstring)/bool safety]) - --send_callback is a once callback and is safety assurance, blocking until buffer_or_lstring is sent only if safety is true. +10. result, buffer = **tcp.read**(socket[, read_callback(result, buffer, socket)]) + *`--read_callback is a continues callback, blocking if read_callback is nil.`* -12. tcp.set_rwopt(socket, option_table) - --set tcp_socket read and write options --e.g. { "read_head_endian" = "L", "read_head_bytes" = 2, "read_head_max" = 65535, } +11. result, error = **tcp.write**(socket/fd, buffer/string[, write_callback(result, error, socket/fd, buffer/string)/bool safety]) + *`--write a tcp socket or directly on a tcp fd(tcp.fd()) where the socket must enable shared write in advance(call tcp.set_wshared(socket, true)).`* + *`--write_callback is a once callback and is safety assurance, blocking until buffer_or_lstring is sent only if safety is true.`* -13. option_table = tcp.get_rwopt(socket) - --get tcp_socket read and write options --e.g. { "read_head_endian" = "L", "read_head_bytes" = 2, "read_head_max" = 65535, } +12. **tcp.set_rwopt**(socket, option_table) + *`--set tcp_socket read and write options --e.g. { "read_head_endian" = "L", "read_head_bytes" = 2, "read_head_max" = 65535, "write_head_endian" = "L", "write_head_bytes" = 2, "write_head_max" = 65535,}`* -14. tcp.set_nodelay(socket, enable) +13. option_table = **tcp.get_rwopt**(socket) + *`--get tcp_socket read and write options --e.g. { "read_head_endian" = "L", "read_head_bytes" = 2, "read_head_max" = 65535, "write_head_endian" = "L", "write_head_bytes" = 2, "write_head_max" = 65535,}`* + +14. **tcp.set_nodelay**(socket, enable) + *`--change tcp socket write 'nodelay' option. Enable nodelay if 'enable' is true or disable it if 'enable' is false.`* -### context api -1. result, error = context.send(handle, data) --QUERY +15. **tcp.set_wshared**(socket, enable) + *`--change tcp socket write shared option. Enable write shared if 'enable' is true or disable it if 'enable' is false. tcp.set_rwopt will be choked or invalid if write shared is enabled. So if you want to call both tcp.set_wshared and tcp.set_rwopt, call tcp.set_rwopt first!`* -2. result, query_data = context.query(handle, data[, timeout [, query_callback(result, query_data, handle, data[, timeout])]]) --QUERY(real query) - --query_callback is a once callback, blocking if callback is nil. - -3. result, error = context.reply(handle, session, data) --REPLY +16. local_addr = **tcp.local_addr**(socket) + *`--socket can't be a listen socket.`* -4. result, data, recv_handle, session = context.recv(handle[, timeout]) - --recv_callback is a continues callback, blocking if callback is nil. - -5. result, data, recv_handle, session = context.recv(handle[, recv_callback(result, data, recv_handle, session, handle)]) - --recv_callback is a continues callback, blocking if callback is nil. +17. remote_addr = **tcp.remote_addr**(socket) + *`--socket can't be a listen socket.`* + +18. local_port = **tcp.local_port**(socket) + *`--socket can't be a listen socket`* + +19. remote_port = **tcp.remote_port**(socket) + *`--socket can't be a listen socket.`* + +20. **tcp.close**(socket) + *`--close the tcp socket directly.`* + +21. is_closed = **tcp.is_closed**(socket) + *`--check whether the tcp socket is closed.`* + +22. fd = **tcp.fd**(socket) + *`--get tcp socket lua fd`* + +23. context_id = **tcp.fd_src**(fd) + *`--get socket fd source context id`* + +### udp api + +1. result, socket = **udp.open**(addr, port, [callback(result, socket, addr, port)]) + *`--open a udp socket on a ipv4 address.`* + *`--callback is a once callback, blocking if callback is nil.`* + +2. result, socket = **udp.open**([callback(result, socket)]) + *`--open a udp socket on "0.0.0.0" with a random port.`* + *`--callback is a once callback, blocking if callback is nil.`* + +3. result, socket = **udp.open6**(addr, port, [callback(result, socket, addr, port)]) + *`--open a udp socket on a ipv6 address.`* + *`--callback is a once callback, blocking if callback is nil.`* + +4. result, socket = **udp.open6**([callback(result, socket)]) + *`--open a udp socket on "::" with a random port.`* + *`--callback is a once callback, blocking if callback is nil.`* + +5. result, error = **udp.write**(socket/fd, buffer/string, remote_addr, remote_port[, write_callback(result, error, socket/fd, buffer/string, remote_addr, remote_port)/bool safety]) + *`--write a udp socket or directly on a udp fd(udp.fd()) where the socket must enable shared write in advance(call udp.set_wshared(socket, true)).`* + *`--write_callback is a once callback and is safety assurance, blocking until buffer_or_lstring is sent only if safety is true.`* + +6. result, error = **udp.write6**(socket/fd, buffer/string, remote_addr, remote_port[, write_callback(result, error, socket/fd, buffer/string, remote_addr, remote_port)/bool safety]) + *`--write a udp socket or directly on a udp fd(udp.fd()) where the socket must enable shared write in advance(call udp.set_wshared(socket, true)).`* + *`--write_callback is a once callback and is safety assurance, blocking until buffer_or_lstring is sent only if safety is true.`* -6. result, error = context.wait(handle[, timeout[, callback(result, error, handle[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. +7. result, buffer, remote_addr, remote_port, remote_ipv6 = **udp.read**(socket, read_callback(result, buffer, remote_addr, remote_port, remote_ipv6, socket)) + *`--udp max datagram read pack size: SHARED_READ_BUFFER_SIZE(64 * 1024).`* + *`--read_callback is a continues callback, blocking if read_callback is nil.`* + +8. **udp.set_wshared**(socket, enable) + *`--change udp socket write shared option. Enable write shared if 'enable' is true or disable it if 'enable' is false.`* + +9. **udp.close**(socket) + *`--close the udp socket directly.`* + +10. local_addr = **udp.local_addr**(socket) + +11. remote_addr = **udp.remote_addr**(socket) + +12. local_port = **udp.local_port**(socket) + +13. remote_port = **udp.remote_port**(socket) + +14. is_closed = **udp.is_closed**(socket) + *`--check whether the udp socket is closed.`* + +15. fd = **udp.fd**(socket) + *`--get udp socket lua fd`* + +16. context_id = **udp.fd_src**(fd) + *`--get socket fd source context id`* ### timer api -1. timer.sleep(seconds) -2. timer.timeout(seconds, ..., callback(...)) +1. **timer.sleep**(seconds) + *`--blocking for seconds.`* + +2. **timer.timeout**(seconds, ..., callback(...)) + *`--make a tiimeout callback in seconds.`* + +3. **timer.loop**(interval, repeat_time, ..., callback(...)) + *`--make a repeated callback. The first time callback will be triggered in interval seconds and then repeated in repeat_time`* + +### buffer api + +1. buffer = **buffer.new**([string]) + *`--make a buffer. buffer will be initialized as string if string is not nil.`* + +2. buffer = **buffer.new**(capacity[, string]) + *`--make a buffer with capacity. buffer will be initialized as string if string is not nil.`* + +3. buffer = **buffer.new**(buffer) + *`--make a buffer initialized with another buffer.`* + +4. result = **buffer.append**(buffer, string/buffer1) + *`--append string/buffer1 to buffer.`* + +5. split_buffer = **buffer.split**(buffer[, split_length]) + *`--split buffer. The front split_length bytes in buffer will be splitted out as split_buffer and the front split_length bytes will be removed from buffer. The default value for split_length is the origin buffer length.`* + +6. result = **buffer.clear**(buffer) + *`--clear data in buffer.`* + +7. length = **buffer.length**(buffer) + *`--return the buffer length.`* + +8. unfill = **buffer.unfill**(buffer) + *`--return the unfilled(or usefull) space in buffer.`* + +9. position = **buffer.find**(buffer, string) + *`--find string in buffer and return the start position found.`* + +10. string = **buffer.tostring**(buffer) + *`--convert buffer to lua string. `* -3. timer.loop(interval, repeat_time, ..., callback(...)) +11. is_valid = **buffer.valid**(buffer) + *`--check whether the buffer is valid, in other words whether the buffer is already released. `* -### buffer api +12. **buffer.release**(buffer) + *`--release the buffer. There may be some unexpected error if you operate on a released buffer, but it won't crash the program. `* \ No newline at end of file diff --git a/UpgradeLog.htm b/UpgradeLog.htm deleted file mode 100644 index f78437e..0000000 Binary files a/UpgradeLog.htm and /dev/null differ diff --git a/deps/lua/Makefile b/deps/lua/Makefile index d6f872e..d445ce9 100644 --- a/deps/lua/Makefile +++ b/deps/lua/Makefile @@ -6,8 +6,8 @@ # Your platform. See PLATS for possible values. PLAT= none -CC= gcc -CFLAGS= -O2 -Wall -DLUA_COMPAT_ALL $(SYSCFLAGS) $(MYCFLAGS) +CC= gcc -std=gnu99 +CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_2 -DLUA_COMPAT_5_1 $(SYSCFLAGS) $(MYCFLAGS) LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) LIBS= -lm $(SYSLIBS) $(MYLIBS) @@ -26,7 +26,7 @@ MYOBJS= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= -PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris +PLATS= aix c89 bsd freebsd generic linux macosx mingw posix solaris LUA_A= libnlua.a LUA_SO= libnlua.so @@ -34,7 +34,7 @@ CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \ lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \ ltm.o lundump.o lvm.o lzio.o LIB_O= lauxlib.o lbaselib.o lbitlib.o lcorolib.o ldblib.o liolib.o \ - lmathlib.o loslib.o lstrlib.o ltablib.o loadlib.o linit.o + lmathlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o loadlib.o linit.o BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS) LUA_T= lua @@ -95,12 +95,16 @@ none: aix: $(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall" -ansi: - $(MAKE) $(ALL) SYSCFLAGS="-DLUA_ANSI" - bsd: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E" +c89: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89" + @echo '' + @echo '*** C89 does not guarantee 64-bit integers for Lua.' + @echo '' + + freebsd: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -lreadline" @@ -113,7 +117,7 @@ macosx: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX" SYSLIBS="-lreadline" CC=cc mingw: - $(MAKE) "LUA_A=nlua52.dll" "LUA_T=lua.exe" \ + $(MAKE) "LUA_A=nlua.dll" "LUA_T=lua.exe" \ "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ "SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe $(MAKE) "LUAC_T=luac.exe" luac.exe @@ -122,70 +126,76 @@ posix: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX" solaris: - $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl" # list targets that do not create files (but not all makes understand .PHONY) .PHONY: all $(PLATS) default o a clean depend echo none # DO NOT DELETE -lapi.o: lapi.c lua.h luaconf.h lapi.h llimits.h lstate.h lobject.h ltm.h \ - lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h ltable.h lundump.h \ +lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ + lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \ + ltable.h lundump.h lvm.h +lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h +lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lbitlib.o: lbitlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ + llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ + ldo.h lgc.h lstring.h ltable.h lvm.h +lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h +ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ + lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \ + ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h +ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ + lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \ + lparser.h lstring.h ltable.h lundump.h lvm.h +ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \ + ltm.h lzio.h lmem.h lundump.h +lfunc.o: lfunc.c lprefix.h lua.h luaconf.h lfunc.h lobject.h llimits.h \ + lgc.h lstate.h ltm.h lzio.h lmem.h +lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h +linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h +liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \ + lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \ + lstring.h ltable.h +lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h +loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \ + ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \ lvm.h -lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h -lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h -lbitlib.o: lbitlib.c lua.h luaconf.h lauxlib.h lualib.h -lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ - lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \ - lstring.h ltable.h lvm.h -lcorolib.o: lcorolib.c lua.h luaconf.h lauxlib.h lualib.h -lctype.o: lctype.c lctype.h lua.h luaconf.h llimits.h -ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h -ldebug.o: ldebug.c lua.h luaconf.h lapi.h llimits.h lstate.h lobject.h \ - ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h ldebug.h ldo.h \ - lfunc.h lstring.h lgc.h ltable.h lvm.h -ldo.o: ldo.c lua.h luaconf.h lapi.h llimits.h lstate.h lobject.h ltm.h \ - lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h \ - lstring.h ltable.h lundump.h lvm.h -ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \ - lzio.h lmem.h lundump.h -lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h \ - lstate.h ltm.h lzio.h lmem.h -lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h -linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h -liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h -llex.o: llex.c lua.h luaconf.h lctype.h llimits.h ldo.h lobject.h \ - lstate.h ltm.h lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h -lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h -lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h lgc.h -loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h -lobject.o: lobject.c lua.h luaconf.h lctype.h llimits.h ldebug.h lstate.h \ - lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h lvm.h -lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h -loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h -lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ - lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lfunc.h \ - lstring.h lgc.h ltable.h -lstate.o: lstate.c lua.h luaconf.h lapi.h llimits.h lstate.h lobject.h \ - ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h lstring.h \ - ltable.h -lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \ - ltm.h lzio.h lstring.h lgc.h -lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h -ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h -ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h -ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \ - lmem.h lstring.h lgc.h ltable.h -lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h -luac.o: luac.c lua.h luaconf.h lauxlib.h lobject.h llimits.h lstate.h \ - ltm.h lzio.h lmem.h lundump.h ldebug.h lopcodes.h -lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \ - llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h -lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h -lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \ - lzio.h - +lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h +loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ + llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ + ldo.h lfunc.h lstring.h lgc.h ltable.h +lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ + lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \ + lstring.h ltable.h +lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ + lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h +lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h +ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h ltable.h lvm.h +lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h lobject.h llimits.h \ + lstate.h ltm.h lzio.h lmem.h lundump.h ldebug.h lopcodes.h +lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ + lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \ + lundump.h +lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \ + ltable.h lvm.h +lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \ + lobject.h ltm.h lzio.h + +# (end of Makefile) diff --git a/deps/lua/README b/deps/lua/README index 9e1aea2..11c03d2 100644 --- a/deps/lua/README +++ b/deps/lua/README @@ -1,3 +1,3 @@ -This is a modify version of lua 5.2.3 (http://www.lua.org/ftp/lua-5.2.3.tar.gz) . +This is a modify version of lua 5.3.2. For detail : http://lua-users.org/lists/lua-l/2014-03/msg00489.html diff --git a/deps/lua/lapi.c b/deps/lua/lapi.c index b2510ab..4b1d3db 100644 --- a/deps/lua/lapi.c +++ b/deps/lua/lapi.c @@ -1,16 +1,18 @@ /* -** $Id: lapi.c,v 2.171.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lapi.c,v 2.257 2015/11/02 18:48:07 roberto Exp $ ** Lua API ** See Copyright Notice in lua.h */ +#define lapi_c +#define LUA_CORE + +#include "lprefix.h" + #include #include -#define lapi_c -#define LUA_CORE - #include "lua.h" #include "lapi.h" @@ -26,6 +28,7 @@ #include "ltm.h" #include "lundump.h" #include "lvm.h" +#include "lfunc.h" @@ -43,13 +46,16 @@ const char lua_ident[] = /* test for pseudo index */ #define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) +/* test for upvalue */ +#define isupvalue(i) ((i) < LUA_REGISTRYINDEX) + /* test for valid but not pseudo index */ #define isstackindex(i, o) (isvalid(o) && !ispseudo(i)) -#define api_checkvalidindex(L, o) api_check(L, isvalid(o), "invalid index") +#define api_checkvalidindex(l,o) api_check(l, isvalid(o), "invalid index") -#define api_checkstackindex(L, i, o) \ - api_check(L, isstackindex(i, o), "index not in the stack") +#define api_checkstackindex(l, i, o) \ + api_check(l, isstackindex(i, o), "index not in the stack") static TValue *index2addr (lua_State *L, int idx) { @@ -89,21 +95,22 @@ static void growstack (lua_State *L, void *ud) { } -LUA_API int lua_checkstack (lua_State *L, int size) { +LUA_API int lua_checkstack (lua_State *L, int n) { int res; CallInfo *ci = L->ci; lua_lock(L); - if (L->stack_last - L->top > size) /* stack large enough? */ + api_check(L, n >= 0, "negative 'n'"); + if (L->stack_last - L->top > n) /* stack large enough? */ res = 1; /* yes; check is OK */ else { /* no; need to grow stack */ int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; - if (inuse > LUAI_MAXSTACK - size) /* can grow without overflow? */ + if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ res = 0; /* no */ else /* try to grow stack */ - res = (luaD_rawrunprotected(L, &growstack, &size) == LUA_OK); + res = (luaD_rawrunprotected(L, &growstack, &n) == LUA_OK); } - if (res && ci->top < L->top + size) - ci->top = L->top + size; /* adjust frame top */ + if (res && ci->top < L->top + n) + ci->top = L->top + n; /* adjust frame top */ lua_unlock(L); return res; } @@ -115,10 +122,11 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); - api_check(from, to->ci->top - to->top >= n, "not enough elements to move"); + api_check(from, to->ci->top - to->top >= n, "stack overflow"); from->top -= n; for (i = 0; i < n; i++) { - setobj2s(to, to->top++, from->top + i); + setobj2s(to, to->top, from->top + i); + to->top++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } @@ -153,7 +161,7 @@ LUA_API const lua_Number *lua_version (lua_State *L) { LUA_API int lua_absindex (lua_State *L, int idx) { return (idx > 0 || ispseudo(idx)) ? idx - : cast_int(L->top - L->ci->func + idx); + : cast_int(L->top - L->ci->func) + idx; } @@ -173,61 +181,56 @@ LUA_API void lua_settop (lua_State *L, int idx) { } else { api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); - L->top += idx+1; /* `subtract' index (index is negative) */ + L->top += idx+1; /* 'subtract' index (index is negative) */ } lua_unlock(L); } -LUA_API void lua_remove (lua_State *L, int idx) { - StkId p; - lua_lock(L); - p = index2addr(L, idx); - api_checkstackindex(L, idx, p); - while (++p < L->top) setobjs2s(L, p-1, p); - L->top--; - lua_unlock(L); +/* +** Reverse the stack segment from 'from' to 'to' +** (auxiliary to 'lua_rotate') +*/ +static void reverse (lua_State *L, StkId from, StkId to) { + for (; from < to; from++, to--) { + TValue temp; + setobj(L, &temp, from); + setobjs2s(L, from, to); + setobj2s(L, to, &temp); + } } -LUA_API void lua_insert (lua_State *L, int idx) { - StkId p; - StkId q; +/* +** Let x = AB, where A is a prefix of length 'n'. Then, +** rotate x n == BA. But BA == (A^r . B^r)^r. +*/ +LUA_API void lua_rotate (lua_State *L, int idx, int n) { + StkId p, t, m; lua_lock(L); - p = index2addr(L, idx); + t = L->top - 1; /* end of stack segment being rotated */ + p = index2addr(L, idx); /* start of segment */ api_checkstackindex(L, idx, p); - for (q = L->top; q > p; q--) /* use L->top as a temporary */ - setobjs2s(L, q, q - 1); - setobjs2s(L, p, L->top); + api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); + m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ + reverse(L, p, m); /* reverse the prefix with length 'n' */ + reverse(L, m + 1, t); /* reverse the suffix */ + reverse(L, p, t); /* reverse the entire segment */ lua_unlock(L); } -static void moveto (lua_State *L, TValue *fr, int idx) { - TValue *to = index2addr(L, idx); +LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { + TValue *fr, *to; + lua_lock(L); + fr = index2addr(L, fromidx); + to = index2addr(L, toidx); api_checkvalidindex(L, to); setobj(L, to, fr); - if (idx < LUA_REGISTRYINDEX) /* function upvalue? */ + if (isupvalue(toidx)) /* function upvalue? */ luaC_barrier(L, clCvalue(L->ci->func), fr); /* LUA_REGISTRYINDEX does not need gc barrier (collector revisits it before finishing collection) */ -} - - -LUA_API void lua_replace (lua_State *L, int idx) { - lua_lock(L); - api_checknelems(L, 1); - moveto(L, L->top - 1, idx); - L->top--; - lua_unlock(L); -} - - -LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { - TValue *fr; - lua_lock(L); - fr = index2addr(L, fromidx); - moveto(L, fr, toidx); lua_unlock(L); } @@ -248,12 +251,13 @@ LUA_API void lua_pushvalue (lua_State *L, int idx) { LUA_API int lua_type (lua_State *L, int idx) { StkId o = index2addr(L, idx); - return (isvalid(o) ? ttypenv(o) : LUA_TNONE); + return (isvalid(o) ? ttnov(o) : LUA_TNONE); } LUA_API const char *lua_typename (lua_State *L, int t) { UNUSED(L); + api_check(L, LUA_TNONE <= t && t < LUA_NUMTAGS, "invalid tag"); return ttypename(t); } @@ -264,22 +268,28 @@ LUA_API int lua_iscfunction (lua_State *L, int idx) { } +LUA_API int lua_isinteger (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + return ttisinteger(o); +} + + LUA_API int lua_isnumber (lua_State *L, int idx) { - TValue n; + lua_Number n; const TValue *o = index2addr(L, idx); return tonumber(o, &n); } LUA_API int lua_isstring (lua_State *L, int idx) { - int t = lua_type(L, idx); - return (t == LUA_TSTRING || t == LUA_TNUMBER); + const TValue *o = index2addr(L, idx); + return (ttisstring(o) || cvt2str(o)); } LUA_API int lua_isuserdata (lua_State *L, int idx) { const TValue *o = index2addr(L, idx); - return (ttisuserdata(o) || ttislightuserdata(o)); + return (ttisfulluserdata(o) || ttislightuserdata(o)); } @@ -291,24 +301,17 @@ LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { LUA_API void lua_arith (lua_State *L, int op) { - StkId o1; /* 1st operand */ - StkId o2; /* 2nd operand */ lua_lock(L); - if (op != LUA_OPUNM) /* all other operations expect two operands */ - api_checknelems(L, 2); - else { /* for unary minus, add fake 2nd operand */ + if (op != LUA_OPUNM && op != LUA_OPBNOT) + api_checknelems(L, 2); /* all other operations expect two operands */ + else { /* for unary operations, add fake 2nd operand */ api_checknelems(L, 1); setobjs2s(L, L->top, L->top - 1); - L->top++; - } - o1 = L->top - 2; - o2 = L->top - 1; - if (ttisnumber(o1) && ttisnumber(o2)) { - setnvalue(o1, luaO_arith(op, nvalue(o1), nvalue(o2))); + api_incr_top(L); } - else - luaV_arith(L, o1, o1, o2, cast(TMS, op - LUA_OPADD + TM_ADD)); - L->top--; + /* first operand at top - 2, second at top - 1; result go to top - 2 */ + luaO_arith(L, op, L->top - 2, L->top - 1, L->top - 2); + L->top--; /* remove second operand */ lua_unlock(L); } @@ -321,7 +324,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { o2 = index2addr(L, index2); if (isvalid(o1) && isvalid(o2)) { switch (op) { - case LUA_OPEQ: i = equalobj(L, o1, o2); break; + case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break; case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; default: api_check(L, 0, "invalid option"); @@ -332,51 +335,33 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { } -LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum) { - TValue n; - const TValue *o = index2addr(L, idx); - if (tonumber(o, &n)) { - if (isnum) *isnum = 1; - return nvalue(o); - } - else { - if (isnum) *isnum = 0; - return 0; - } +LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { + size_t sz = luaO_str2num(s, L->top); + if (sz != 0) + api_incr_top(L); + return sz; } -LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum) { - TValue n; +LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) { + lua_Number n; const TValue *o = index2addr(L, idx); - if (tonumber(o, &n)) { - lua_Integer res; - lua_Number num = nvalue(o); - lua_number2integer(res, num); - if (isnum) *isnum = 1; - return res; - } - else { - if (isnum) *isnum = 0; - return 0; - } + int isnum = tonumber(o, &n); + if (!isnum) + n = 0; /* call to 'tonumber' may change 'n' even if it fails */ + if (pisnum) *pisnum = isnum; + return n; } -LUA_API lua_Unsigned lua_tounsignedx (lua_State *L, int idx, int *isnum) { - TValue n; +LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) { + lua_Integer res; const TValue *o = index2addr(L, idx); - if (tonumber(o, &n)) { - lua_Unsigned res; - lua_Number num = nvalue(o); - lua_number2unsigned(res, num); - if (isnum) *isnum = 1; - return res; - } - else { - if (isnum) *isnum = 0; - return 0; - } + int isnum = tointeger(o, &res); + if (!isnum) + res = 0; /* call to 'tointeger' may change 'n' even if it fails */ + if (pisnum) *pisnum = isnum; + return res; } @@ -389,25 +374,27 @@ LUA_API int lua_toboolean (lua_State *L, int idx) { LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { StkId o = index2addr(L, idx); if (!ttisstring(o)) { - lua_lock(L); /* `luaV_tostring' may create a new string */ - if (!luaV_tostring(L, o)) { /* conversion failed? */ + if (!cvt2str(o)) { /* not convertible? */ if (len != NULL) *len = 0; - lua_unlock(L); return NULL; } + lua_lock(L); /* 'luaO_tostring' may create a new string */ luaC_checkGC(L); o = index2addr(L, idx); /* previous call may reallocate the stack */ + luaO_tostring(L, o); lua_unlock(L); } - if (len != NULL) *len = tsvalue(o)->len; + if (len != NULL) + *len = vslen(o); return svalue(o); } LUA_API size_t lua_rawlen (lua_State *L, int idx) { StkId o = index2addr(L, idx); - switch (ttypenv(o)) { - case LUA_TSTRING: return tsvalue(o)->len; + switch (ttype(o)) { + case LUA_TSHRSTR: return tsvalue(o)->shrlen; + case LUA_TLNGSTR: return tsvalue(o)->u.lnglen; case LUA_TUSERDATA: return uvalue(o)->len; case LUA_TTABLE: return luaH_getn(hvalue(o)); default: return 0; @@ -426,8 +413,8 @@ LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { LUA_API void *lua_touserdata (lua_State *L, int idx) { StkId o = index2addr(L, idx); - switch (ttypenv(o)) { - case LUA_TUSERDATA: return (rawuvalue(o) + 1); + switch (ttnov(o)) { + case LUA_TUSERDATA: return getudatamem(uvalue(o)); case LUA_TLIGHTUSERDATA: return pvalue(o); default: return NULL; } @@ -448,9 +435,8 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) { case LUA_TCCL: return clCvalue(o); case LUA_TLCF: return cast(void *, cast(size_t, fvalue(o))); case LUA_TTHREAD: return thvalue(o); - case LUA_TUSERDATA: - case LUA_TLIGHTUSERDATA: - return lua_touserdata(L, idx); + case LUA_TUSERDATA: return getudatamem(uvalue(o)); + case LUA_TLIGHTUSERDATA: return pvalue(o); default: return NULL; } } @@ -472,9 +458,7 @@ LUA_API void lua_pushnil (lua_State *L) { LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { lua_lock(L); - setnvalue(L->top, n); - luai_checknum(L, L->top, - luaG_runerror(L, "C API - attempt to push a signaling NaN")); + setfltvalue(L->top, n); api_incr_top(L); lua_unlock(L); } @@ -482,27 +466,22 @@ LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { lua_lock(L); - setnvalue(L->top, cast_num(n)); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushunsigned (lua_State *L, lua_Unsigned u) { - lua_Number n; - lua_lock(L); - n = lua_unsigned2number(u); - setnvalue(L->top, n); + setivalue(L->top, n); api_incr_top(L); lua_unlock(L); } +/* +** Pushes on the stack a string with given length. Avoid using 's' when +** 'len' == 0 (as 's' can be NULL in that case), due to later use of +** 'memcmp' and 'memcpy'. +*/ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); luaC_checkGC(L); - ts = luaS_newlstr(L, s, len); + ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); setsvalue2s(L, L->top, ts); api_incr_top(L); lua_unlock(L); @@ -511,20 +490,19 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { LUA_API const char *lua_pushstring (lua_State *L, const char *s) { - if (s == NULL) { - lua_pushnil(L); - return NULL; - } + lua_lock(L); + if (s == NULL) + setnilvalue(L->top); else { TString *ts; - lua_lock(L); luaC_checkGC(L); ts = luaS_new(L, s); setsvalue2s(L, L->top, ts); - api_incr_top(L); - lua_unlock(L); - return getstr(ts); + s = getstr(ts); /* internal copy's address */ } + api_incr_top(L); + lua_unlock(L); + return s; } @@ -558,15 +536,17 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { setfvalue(L->top, fn); } else { - Closure *cl; + CClosure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); luaC_checkGC(L); cl = luaF_newCclosure(L, n); - cl->c.f = fn; + cl->f = fn; L->top -= n; - while (n--) - setobj2n(L, &cl->c.upvalue[n], L->top + n); + while (n--) { + setobj2n(L, &cl->upvalue[n], L->top + n); + /* does not need barrier because closure is white */ + } setclCvalue(L, L->top, cl); } api_incr_top(L); @@ -605,48 +585,77 @@ LUA_API int lua_pushthread (lua_State *L) { */ -LUA_API void lua_getglobal (lua_State *L, const char *var) { +static int auxgetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *aux; + TString *str = luaS_new(L, k); + if (luaV_fastget(L, t, str, aux, luaH_getstr)) { + setobj2s(L, L->top, aux); + api_incr_top(L); + } + else { + setsvalue2s(L, L->top, str); + api_incr_top(L); + luaV_finishget(L, t, L->top - 1, L->top - 1, aux); + } + lua_unlock(L); + return ttnov(L->top - 1); +} + + +LUA_API int lua_getglobal (lua_State *L, const char *name) { Table *reg = hvalue(&G(L)->l_registry); - const TValue *gt; /* global table */ lua_lock(L); - gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - setsvalue2s(L, L->top++, luaS_new(L, var)); - luaV_gettable(L, gt, L->top - 1, L->top - 1); - lua_unlock(L); + return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); } -LUA_API void lua_gettable (lua_State *L, int idx) { +LUA_API int lua_gettable (lua_State *L, int idx) { StkId t; lua_lock(L); t = index2addr(L, idx); luaV_gettable(L, t, L->top - 1, L->top - 1); lua_unlock(L); + return ttnov(L->top - 1); } -LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { +LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { + lua_lock(L); + return auxgetstr(L, index2addr(L, idx), k); +} + + +LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { StkId t; + const TValue *aux; lua_lock(L); t = index2addr(L, idx); - setsvalue2s(L, L->top, luaS_new(L, k)); - api_incr_top(L); - luaV_gettable(L, t, L->top - 1, L->top - 1); + if (luaV_fastget(L, t, n, aux, luaH_getint)) { + setobj2s(L, L->top, aux); + api_incr_top(L); + } + else { + setivalue(L->top, n); + api_incr_top(L); + luaV_finishget(L, t, L->top - 1, L->top - 1, aux); + } lua_unlock(L); + return ttnov(L->top - 1); } -LUA_API void lua_rawget (lua_State *L, int idx) { +LUA_API int lua_rawget (lua_State *L, int idx) { StkId t; lua_lock(L); t = index2addr(L, idx); api_check(L, ttistable(t), "table expected"); setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); lua_unlock(L); + return ttnov(L->top - 1); } -LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { +LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { StkId t; lua_lock(L); t = index2addr(L, idx); @@ -654,10 +663,11 @@ LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { setobj2s(L, L->top, luaH_getint(hvalue(t), n)); api_incr_top(L); lua_unlock(L); + return ttnov(L->top - 1); } -LUA_API void lua_rawgetp (lua_State *L, int idx, const void *p) { +LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { StkId t; TValue k; lua_lock(L); @@ -667,6 +677,7 @@ LUA_API void lua_rawgetp (lua_State *L, int idx, const void *p) { setobj2s(L, L->top, luaH_get(hvalue(t), &k)); api_incr_top(L); lua_unlock(L); + return ttnov(L->top - 1); } @@ -685,11 +696,11 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; - Table *mt = NULL; - int res; + Table *mt; + int res = 0; lua_lock(L); obj = index2addr(L, objindex); - switch (ttypenv(obj)) { + switch (ttnov(obj)) { case LUA_TTABLE: mt = hvalue(obj)->metatable; break; @@ -697,12 +708,10 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) { mt = uvalue(obj)->metatable; break; default: - mt = G(L)->mt[ttypenv(obj)]; + mt = G(L)->mt[ttnov(obj)]; break; } - if (mt == NULL) - res = 0; - else { + if (mt != NULL) { sethvalue(L, L->top, mt); api_incr_top(L); res = 1; @@ -712,17 +721,15 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) { } -LUA_API void lua_getuservalue (lua_State *L, int idx) { +LUA_API int lua_getuservalue (lua_State *L, int idx) { StkId o; lua_lock(L); o = index2addr(L, idx); - api_check(L, ttisuserdata(o), "userdata expected"); - if (uvalue(o)->env) { - sethvalue(L, L->top, uvalue(o)->env); - } else - setnilvalue(L->top); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + getuservalue(L, uvalue(o), L->top); api_incr_top(L); lua_unlock(L); + return ttnov(L->top - 1); } @@ -730,17 +737,29 @@ LUA_API void lua_getuservalue (lua_State *L, int idx) { ** set functions (stack -> Lua) */ +/* +** t[k] = value at the top of the stack (where 'k' is a string) +*/ +static void auxsetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *aux; + TString *str = luaS_new(L, k); + api_checknelems(L, 1); + if (luaV_fastset(L, t, str, aux, luaH_getstr, L->top - 1)) + L->top--; /* pop value */ + else { + setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ + api_incr_top(L); + luaV_finishset(L, t, L->top - 1, L->top - 2, aux); + L->top -= 2; /* pop value and key */ + } + lua_unlock(L); /* lock done by caller */ +} -LUA_API void lua_setglobal (lua_State *L, const char *var) { + +LUA_API void lua_setglobal (lua_State *L, const char *name) { Table *reg = hvalue(&G(L)->l_registry); - const TValue *gt; /* global table */ - lua_lock(L); - api_checknelems(L, 1); - gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - setsvalue2s(L, L->top++, luaS_new(L, var)); - luaV_settable(L, gt, L->top - 1, L->top - 2); - L->top -= 2; /* pop value and key */ - lua_unlock(L); + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); } @@ -756,54 +775,69 @@ LUA_API void lua_settable (lua_State *L, int idx) { LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, index2addr(L, idx), k); +} + + +LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { StkId t; + const TValue *aux; lua_lock(L); api_checknelems(L, 1); t = index2addr(L, idx); - setsvalue2s(L, L->top++, luaS_new(L, k)); - luaV_settable(L, t, L->top - 1, L->top - 2); - L->top -= 2; /* pop value and key */ + if (luaV_fastset(L, t, n, aux, luaH_getint, L->top - 1)) + L->top--; /* pop value */ + else { + setivalue(L->top, n); + api_incr_top(L); + luaV_finishset(L, t, L->top - 1, L->top - 2, aux); + L->top -= 2; /* pop value and key */ + } lua_unlock(L); } LUA_API void lua_rawset (lua_State *L, int idx) { - StkId t; + StkId o; + TValue *slot; lua_lock(L); api_checknelems(L, 2); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); - invalidateTMcache(hvalue(t)); - luaC_barrierback(L, gcvalue(t), L->top-1); + o = index2addr(L, idx); + api_check(L, ttistable(o), "table expected"); + slot = luaH_set(L, hvalue(o), L->top - 2); + setobj2t(L, slot, L->top - 1); + invalidateTMcache(hvalue(o)); + luaC_barrierback(L, hvalue(o), L->top-1); L->top -= 2; lua_unlock(L); } -LUA_API void lua_rawseti (lua_State *L, int idx, int n) { - StkId t; +LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { + StkId o; lua_lock(L); api_checknelems(L, 1); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - luaH_setint(L, hvalue(t), n, L->top - 1); - luaC_barrierback(L, gcvalue(t), L->top-1); + o = index2addr(L, idx); + api_check(L, ttistable(o), "table expected"); + luaH_setint(L, hvalue(o), n, L->top - 1); + luaC_barrierback(L, hvalue(o), L->top-1); L->top--; lua_unlock(L); } LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { - StkId t; - TValue k; + StkId o; + TValue k, *slot; lua_lock(L); api_checknelems(L, 1); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); + o = index2addr(L, idx); + api_check(L, ttistable(o), "table expected"); setpvalue(&k, cast(void *, p)); - setobj2t(L, luaH_set(L, hvalue(t), &k), L->top - 1); - luaC_barrierback(L, gcvalue(t), L->top - 1); + slot = luaH_set(L, hvalue(o), &k); + setobj2t(L, slot, L->top - 1); + luaC_barrierback(L, hvalue(o), L->top - 1); L->top--; lua_unlock(L); } @@ -821,11 +855,11 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { api_check(L, ttistable(L->top - 1), "table expected"); mt = hvalue(L->top - 1); } - switch (ttypenv(obj)) { + switch (ttnov(obj)) { case LUA_TTABLE: { hvalue(obj)->metatable = mt; if (mt) { - luaC_objbarrierback(L, gcvalue(obj), mt); + luaC_objbarrier(L, gcvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; @@ -833,13 +867,13 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { case LUA_TUSERDATA: { uvalue(obj)->metatable = mt; if (mt) { - luaC_objbarrier(L, rawuvalue(obj), mt); + luaC_objbarrier(L, uvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } default: { - G(L)->mt[ttypenv(obj)] = mt; + G(L)->mt[ttnov(obj)] = mt; break; } } @@ -854,21 +888,16 @@ LUA_API void lua_setuservalue (lua_State *L, int idx) { lua_lock(L); api_checknelems(L, 1); o = index2addr(L, idx); - api_check(L, ttisuserdata(o), "userdata expected"); - if (ttisnil(L->top - 1)) - uvalue(o)->env = NULL; - else { - api_check(L, ttistable(L->top - 1), "table expected"); - uvalue(o)->env = hvalue(L->top - 1); - luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); - } + api_check(L, ttisfulluserdata(o), "full userdata expected"); + setuservalue(L, uvalue(o), L->top - 1); + luaC_barrier(L, gcvalue(o), L->top - 1); L->top--; lua_unlock(L); } /* -** `load' and `call' functions (run Lua code) +** 'load' and 'call' functions (run Lua code) */ @@ -877,17 +906,8 @@ LUA_API void lua_setuservalue (lua_State *L, int idx) { "results from function overflow current stack size") -LUA_API int lua_getctx (lua_State *L, int *ctx) { - if (L->ci->callstatus & CIST_YIELDED) { - if (ctx) *ctx = L->ci->u.c.ctx; - return L->ci->u.c.status; - } - else return LUA_OK; -} - - -LUA_API void lua_callk (lua_State *L, int nargs, int nresults, int ctx, - lua_CFunction k) { +LUA_API void lua_callk (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k) { StkId func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), @@ -899,10 +919,10 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults, int ctx, if (k != NULL && L->nny == 0) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ - luaD_call(L, func, nresults, 1); /* do the call */ + luaD_call(L, func, nresults); /* do the call */ } else /* no continuation or no yieldable */ - luaD_call(L, func, nresults, 0); /* just do the call */ + luaD_callnoyield(L, func, nresults); /* just do the call */ adjustresults(L, nresults); lua_unlock(L); } @@ -912,7 +932,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults, int ctx, /* ** Execute a protected call. */ -struct CallS { /* data to `f_call' */ +struct CallS { /* data to 'f_call' */ StkId func; int nresults; }; @@ -920,13 +940,13 @@ struct CallS { /* data to `f_call' */ static void f_call (lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); - luaD_call(L, c->func, c->nresults, 0); + luaD_callnoyield(L, c->func, c->nresults); } LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, - int ctx, lua_CFunction k) { + lua_KContext ctx, lua_KFunction k) { struct CallS c; int status; ptrdiff_t func; @@ -954,12 +974,11 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, ci->u.c.ctx = ctx; /* save context */ /* save information for error recovery */ ci->extra = savestack(L, c.func); - ci->u.c.old_allowhook = L->allowhook; ci->u.c.old_errfunc = L->errfunc; L->errfunc = func; - /* mark that function may do error recovery */ - ci->callstatus |= CIST_YPCALL; - luaD_call(L, c.func, nresults, 1); /* do the call */ + setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ + ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ + luaD_call(L, c.func, nresults); /* do the call */ ci->callstatus &= ~CIST_YPCALL; L->errfunc = ci->u.c.old_errfunc; status = LUA_OK; /* if it is here, there were no errors */ @@ -980,13 +999,13 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, status = luaD_protectedparser(L, &z, chunkname, mode); if (status == LUA_OK) { /* no errors? */ LClosure *f = clLvalue(L->top - 1); /* get newly created function */ - if (f->nupvalues == 1) { /* does it have one upvalue? */ + if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ Table *reg = hvalue(&G(L)->l_registry); const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ setobj(L, f->upvals[0]->v, gt); - luaC_barrier(L, f->upvals[0], gt); + luaC_upvalbarrier(L, f->upvals[0]); } } lua_unlock(L); @@ -1006,7 +1025,7 @@ cloneproto (lua_State *L, const Proto *src) { const TValue *s=&src->k[i]; TValue *o=&f->k[i]; if (ttisstring(s)) { - TString * str = luaS_newlstr(L,svalue(s),tsvalue(s)->len); + TString * str = luaS_newlstr(L,svalue(s),vslen(s)); setsvalue2n(L,o,str); } else { setobj(L,o,s); @@ -1022,9 +1041,8 @@ cloneproto (lua_State *L, const Proto *src) { } LUA_API void lua_clonefunction (lua_State *L, const void * fp) { - int i; - Closure *cl; - const LClosure *f = cast(const LClosure *, fp); + LClosure *cl; + LClosure *f = cast(LClosure *, fp); lua_lock(L); if (f->p->sp->l_G == G(L)) { setclLvalue(L,L->top,f); @@ -1033,21 +1051,18 @@ LUA_API void lua_clonefunction (lua_State *L, const void * fp) { return; } cl = luaF_newLclosure(L,f->nupvalues); - cl->l.p = cloneproto(L, f->p); + cl->p = cloneproto(L, f->p); setclLvalue(L,L->top,cl); api_incr_top(L); - for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */ - UpVal *up = luaF_newupval(L); - cl->l.upvals[i] = up; - luaC_objbarrier(L, cl, up); - } - if (f->nupvalues == 1) { /* does it have one upvalue? */ + luaF_initupvals(L, cl); + + if (cl->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ Table *reg = hvalue(&G(L)->l_registry); const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - /* set global table as 1st upvalue of 'cl' (may be LUA_ENV) */ - setobj(L, cl->l.upvals[0]->v, gt); - luaC_barrier(L, cl->l.upvals[0], gt); + /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ + setobj(L, cl->upvals[0]->v, gt); + luaC_upvalbarrier(L, cl->upvals[0]); } lua_unlock(L); } @@ -1056,17 +1071,14 @@ static void lua_initsharedproto(lua_State *L, Proto *p, const TValue *val, const TValue *gt) { int i; TValue cl; - UpVal *up; - Closure *pcl = luaF_newLclosure(L, 1); - pcl->l.p = p; + LClosure *pcl = luaF_newLclosure(L, 1); + pcl->p = p; p->sp->refnum = 2; //initial as 2 p->sp->closure = pcl; - up = luaF_newupval(L); - pcl->l.upvals[0] = up; - luaC_objbarrier(L, pcl, up); - setobj(L, up->v, gt); - luaC_barrier(L, up, gt); + luaF_initupvals(L, pcl); + setobj(L, pcl->upvals[0]->v, gt); + luaC_upvalbarrier(L, pcl->upvals[0]); setclLvalue(L, &cl, pcl); setobj2t(L, luaH_set(L, hvalue(gt), &cl), val); @@ -1096,7 +1108,7 @@ LUA_API void* lua_initsharedclosure(lua_State *L) { reg = hvalue(&G(L)->l_registry); gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - setnvalue(&val, cast_num(1)); + setivalue(&val, cast_int(1)); setobj2t(L, luaH_set(L, hvalue(gt), cl), &val); invalidateTMcache(hvalue(gt)); --L->top; @@ -1197,14 +1209,14 @@ LUA_API void lua_unrefsharedclosure(void *f) { } #endif -LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) { +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { int status; TValue *o; lua_lock(L); api_checknelems(L, 1); o = L->top - 1; if (isLfunction(o)) - status = luaU_dump(L, getproto(o), writer, data, 0); + status = luaU_dump(L, getproto(o), writer, data, strip); else status = 1; lua_unlock(L); @@ -1250,19 +1262,21 @@ LUA_API int lua_gc (lua_State *L, int what, int data) { break; } case LUA_GCSTEP: { - if (g->gckind == KGC_GEN) { /* generational mode? */ - res = (g->GCestimate == 0); /* true if it will do major collection */ - luaC_forcestep(L); /* do a single step */ + l_mem debt = 1; /* =1 to signal that it did an actual step */ + lu_byte oldrunning = g->gcrunning; + g->gcrunning = 1; /* allow GC to run */ + if (data == 0) { + luaE_setdebt(g, -GCSTEPSIZE); /* to do a "small" step */ + luaC_step(L); } - else { - lu_mem debt = cast(lu_mem, data) * 1024 - GCSTEPSIZE; - if (g->gcrunning) - debt += g->GCdebt; /* include current debt */ - luaE_setdebt(g, debt); - luaC_forcestep(L); - if (g->gcstate == GCSpause) /* end of cycle? */ - res = 1; /* signal it */ + else { /* add 'data' to total debt */ + debt = cast(l_mem, data) * 1024 + g->GCdebt; + luaE_setdebt(g, debt); + luaC_checkGC(L); } + g->gcrunning = oldrunning; /* restore previous state */ + if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ break; } case LUA_GCSETPAUSE: { @@ -1270,13 +1284,9 @@ LUA_API int lua_gc (lua_State *L, int what, int data) { g->gcpause = data; break; } - case LUA_GCSETMAJORINC: { - res = g->gcmajorinc; - g->gcmajorinc = data; - break; - } case LUA_GCSETSTEPMUL: { res = g->gcstepmul; + if (data < 40) data = 40; /* avoid ridiculous low values (and 0) */ g->gcstepmul = data; break; } @@ -1284,14 +1294,6 @@ LUA_API int lua_gc (lua_State *L, int what, int data) { res = g->gcrunning; break; } - case LUA_GCGEN: { /* change collector to generational mode */ - luaC_changemode(L, KGC_GEN); - break; - } - case LUA_GCINC: { /* change collector to incremental mode */ - luaC_changemode(L, KGC_NORMAL); - break; - } default: res = -1; /* invalid option */ } lua_unlock(L); @@ -1379,23 +1381,23 @@ LUA_API void *lua_newuserdata (lua_State *L, size_t size) { Udata *u; lua_lock(L); luaC_checkGC(L); - u = luaS_newudata(L, size, NULL); + u = luaS_newudata(L, size); setuvalue(L, L->top, u); api_incr_top(L); lua_unlock(L); - return u + 1; + return getudatamem(u); } static const char *aux_upvalue (StkId fi, int n, TValue **val, - GCObject **owner) { + CClosure **owner, UpVal **uv) { switch (ttype(fi)) { case LUA_TCCL: { /* C closure */ CClosure *f = clCvalue(fi); if (!(1 <= n && n <= f->nupvalues)) return NULL; *val = &f->upvalue[n-1]; - if (owner) *owner = obj2gco(f); + if (owner) *owner = f; return ""; } case LUA_TLCL: { /* Lua closure */ @@ -1404,9 +1406,9 @@ static const char *aux_upvalue (StkId fi, int n, TValue **val, SharedProto *p = f->p->sp; if (!(1 <= n && n <= p->sizeupvalues)) return NULL; *val = f->upvals[n-1]->v; - if (owner) *owner = obj2gco(f->upvals[n - 1]); + if (uv) *uv = f->upvals[n - 1]; name = p->upvalues[n-1].name; - return (name == NULL) ? "" : getstr(name); + return (name == NULL) ? "(*no name)" : getstr(name); } default: return NULL; /* not a closure */ } @@ -1417,7 +1419,7 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ lua_lock(L); - name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL); + name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL, NULL); if (name) { setobj2s(L, L->top, val); api_incr_top(L); @@ -1430,16 +1432,18 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ - GCObject *owner = NULL; /* to avoid warnings */ + CClosure *owner = NULL; + UpVal *uv = NULL; StkId fi; lua_lock(L); fi = index2addr(L, funcindex); api_checknelems(L, 1); - name = aux_upvalue(fi, n, &val, &owner); + name = aux_upvalue(fi, n, &val, &owner, &uv); if (name) { L->top--; setobj(L, val, L->top); - luaC_barrier(L, owner, L->top); + if (owner) { luaC_barrier(L, owner, L->top); } + else if (uv) { luaC_upvalbarrier(L, uv); } } lua_unlock(L); return name; @@ -1451,7 +1455,7 @@ static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { StkId fi = index2addr(L, fidx); api_check(L, ttisLclosure(fi), "Lua function expected"); f = clLvalue(fi); - api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); + api_check(L, (1 <= n && n <= f->p->sp->sizeupvalues), "invalid upvalue index"); if (pf) *pf = f; return &f->upvals[n - 1]; /* get its upvalue pointer */ } @@ -1481,7 +1485,11 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, LClosure *f1; UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up2 = getupvalref(L, fidx2, n2, NULL); + luaC_upvdeccount(L, *up1); *up1 = *up2; - luaC_objbarrier(L, f1, *up2); + (*up1)->refcount++; + if (upisopen(*up1)) (*up1)->u.open.touched = 1; + luaC_upvalbarrier(L, *up1); } + diff --git a/deps/lua/lapi.h b/deps/lua/lapi.h index c7d34ad..6d36dee 100644 --- a/deps/lua/lapi.h +++ b/deps/lua/lapi.h @@ -1,5 +1,5 @@ /* -** $Id: lapi.h,v 2.7.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lapi.h,v 2.9 2015/03/06 19:49:50 roberto Exp $ ** Auxiliary functions from Lua API ** See Copyright Notice in lua.h */ diff --git a/deps/lua/latomic.h b/deps/lua/latomic.h index d9e0467..2d75f6c 100644 --- a/deps/lua/latomic.h +++ b/deps/lua/latomic.h @@ -16,10 +16,12 @@ ((__GNUC_MINOR__ > (minor)) || ((__GNUC_MINOR__ == (minor)) && \ (__GNUC_PATCHLEVEL__ >= (patch)) ) ) ) ) -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(WINCE) || defined(_WIN32_WCE) -# define CC_MSVC +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(WINCE) || defined(_WIN32_WCE) || defined(_WIN64) +#ifndef CC_MSVC +#define CC_MSVC +#endif #elif CHECK_GNUC(4, 1, 2) /* >= 4.1.2 */ -# define CC_GNUC +#define CC_GNUC #else # error "This Compiler is unsupported!" #endif diff --git a/deps/lua/lauxlib.c b/deps/lua/lauxlib.c index a0e09ca..cf99875 100644 --- a/deps/lua/lauxlib.c +++ b/deps/lua/lauxlib.c @@ -1,9 +1,14 @@ /* -** $Id: lauxlib.c,v 1.248.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lauxlib.c,v 1.284 2015/11/19 19:16:22 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ +#define lauxlib_c +#define LUA_LIB + +#include "lprefix.h" + #include #include @@ -16,9 +21,6 @@ ** Any function declared here could be written as an application function. */ -#define lauxlib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -31,8 +33,8 @@ */ -#define LEVELS1 12 /* size of the first part of the stack */ -#define LEVELS2 10 /* size of the second part of the stack */ +#define LEVELS1 10 /* size of the first part of the stack */ +#define LEVELS2 11 /* size of the second part of the stack */ @@ -64,11 +66,20 @@ static int findfield (lua_State *L, int objidx, int level) { } +/* +** Search for a name for a function in all loaded modules +** (registry._LOADED). +*/ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { int top = lua_gettop(L); lua_getinfo(L, "f", ar); /* push function */ - lua_pushglobaltable(L); + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); if (findfield(L, top + 1, 2)) { + const char *name = lua_tostring(L, -1); + if (strncmp(name, "_G.", 3) == 0) { /* name start with '_G.'? */ + lua_pushstring(L, name + 3); /* push name without prefix */ + lua_remove(L, -2); /* remove original name */ + } lua_copy(L, -1, top + 1); /* move name to proper place */ lua_pop(L, 2); /* remove pushed values */ return 1; @@ -81,24 +92,22 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { static void pushfuncname (lua_State *L, lua_Debug *ar) { - if (*ar->namewhat != '\0') /* is there a name? */ - lua_pushfstring(L, "function " LUA_QS, ar->name); + if (pushglobalfuncname(L, ar)) { /* try first a global name */ + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } + else if (*ar->namewhat != '\0') /* is there a name from code? */ + lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ else if (*ar->what == 'm') /* main? */ lua_pushliteral(L, "main chunk"); - else if (*ar->what == 'C') { - if (pushglobalfuncname(L, ar)) { - lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); - lua_remove(L, -2); /* remove name */ - } - else - lua_pushliteral(L, "?"); - } - else + else if (*ar->what != 'C') /* for Lua functions, use */ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); + else /* nothing left... */ + lua_pushliteral(L, "?"); } -static int countlevels (lua_State *L) { +static int lastlevel (lua_State *L) { lua_Debug ar; int li = 1, le = 1; /* find an upper bound */ @@ -117,14 +126,16 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level) { lua_Debug ar; int top = lua_gettop(L); - int numlevels = countlevels(L1); - int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0; - if (msg) lua_pushfstring(L, "%s\n", msg); + int last = lastlevel(L1); + int n1 = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; + if (msg) + lua_pushfstring(L, "%s\n", msg); + luaL_checkstack(L, 10, NULL); lua_pushliteral(L, "stack traceback:"); while (lua_getstack(L1, level++, &ar)) { - if (level == mark) { /* too many levels? */ + if (n1-- == 0) { /* too many levels? */ lua_pushliteral(L, "\n\t..."); /* add a '...' */ - level = numlevels - LEVELS2; /* and skip to last ones */ + level = last - LEVELS2 + 1; /* and skip to last ones */ } else { lua_getinfo(L1, "Slnt", &ar); @@ -150,33 +161,40 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, ** ======================================================= */ -LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { +LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { lua_Debug ar; if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ - return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); + return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); lua_getinfo(L, "n", &ar); if (strcmp(ar.namewhat, "method") == 0) { - narg--; /* do not count `self' */ - if (narg == 0) /* error is in the self argument itself? */ - return luaL_error(L, "calling " LUA_QS " on bad self (%s)", + arg--; /* do not count 'self' */ + if (arg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling '%s' on bad self (%s)", ar.name, extramsg); } if (ar.name == NULL) ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; - return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", - narg, ar.name, extramsg); + return luaL_error(L, "bad argument #%d to '%s' (%s)", + arg, ar.name, extramsg); } -static int typeerror (lua_State *L, int narg, const char *tname) { - const char *msg = lua_pushfstring(L, "%s expected, got %s", - tname, luaL_typename(L, narg)); - return luaL_argerror(L, narg, msg); +static int typeerror (lua_State *L, int arg, const char *tname) { + const char *msg; + const char *typearg; /* name for the type of the actual argument */ + if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) + typearg = lua_tostring(L, -1); /* use the given type name */ + else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) + typearg = "light userdata"; /* special name for messages */ + else + typearg = luaL_typename(L, arg); /* standard name */ + msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); + return luaL_argerror(L, arg, msg); } -static void tag_error (lua_State *L, int narg, int tag) { - typeerror(L, narg, lua_typename(L, tag)); +static void tag_error (lua_State *L, int arg, int tag) { + typeerror(L, arg, lua_typename(L, tag)); } @@ -222,7 +240,7 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { } -#if !defined(inspectstat) /* { */ +#if !defined(l_inspectstat) /* { */ #if defined(LUA_USE_POSIX) @@ -231,13 +249,13 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { /* ** use appropriate macros to interpret 'pclose' return status */ -#define inspectstat(stat,what) \ +#define l_inspectstat(stat,what) \ if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } #else -#define inspectstat(stat,what) /* no op */ +#define l_inspectstat(stat,what) /* no op */ #endif @@ -249,7 +267,7 @@ LUALIB_API int luaL_execresult (lua_State *L, int stat) { if (stat == -1) /* error? */ return luaL_fileresult(L, 0, NULL); else { - inspectstat(stat, what); /* interpret result */ + l_inspectstat(stat, what); /* interpret result */ if (*what == 'e' && stat == 0) /* successful termination? */ lua_pushboolean(L, 1); else @@ -270,11 +288,12 @@ LUALIB_API int luaL_execresult (lua_State *L, int stat) { */ LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { - luaL_getmetatable(L, tname); /* try to get metatable */ - if (!lua_isnil(L, -1)) /* name already in use? */ + if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ return 0; /* leave previous value on top, but return 0 */ lua_pop(L, 1); - lua_newtable(L); /* create metatable */ + lua_createtable(L, 0, 2); /* create metatable */ + lua_pushstring(L, tname); + lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ return 1; @@ -317,16 +336,16 @@ LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { ** ======================================================= */ -LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def, +LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, const char *const lst[]) { - const char *name = (def) ? luaL_optstring(L, narg, def) : - luaL_checkstring(L, narg); + const char *name = (def) ? luaL_optstring(L, arg, def) : + luaL_checkstring(L, arg); int i; for (i=0; lst[i]; i++) if (strcmp(lst[i], name) == 0) return i; - return luaL_argerror(L, narg, - lua_pushfstring(L, "invalid option " LUA_QS, name)); + return luaL_argerror(L, arg, + lua_pushfstring(L, "invalid option '%s'", name)); } @@ -342,77 +361,71 @@ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { } -LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { - if (lua_type(L, narg) != t) - tag_error(L, narg, t); +LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) { + if (lua_type(L, arg) != t) + tag_error(L, arg, t); } -LUALIB_API void luaL_checkany (lua_State *L, int narg) { - if (lua_type(L, narg) == LUA_TNONE) - luaL_argerror(L, narg, "value expected"); +LUALIB_API void luaL_checkany (lua_State *L, int arg) { + if (lua_type(L, arg) == LUA_TNONE) + luaL_argerror(L, arg, "value expected"); } -LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { - const char *s = lua_tolstring(L, narg, len); - if (!s) tag_error(L, narg, LUA_TSTRING); +LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) { + const char *s = lua_tolstring(L, arg, len); + if (!s) tag_error(L, arg, LUA_TSTRING); return s; } -LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, +LUALIB_API const char *luaL_optlstring (lua_State *L, int arg, const char *def, size_t *len) { - if (lua_isnoneornil(L, narg)) { + if (lua_isnoneornil(L, arg)) { if (len) *len = (def ? strlen(def) : 0); return def; } - else return luaL_checklstring(L, narg, len); + else return luaL_checklstring(L, arg, len); } -LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) { int isnum; - lua_Number d = lua_tonumberx(L, narg, &isnum); + lua_Number d = lua_tonumberx(L, arg, &isnum); if (!isnum) - tag_error(L, narg, LUA_TNUMBER); + tag_error(L, arg, LUA_TNUMBER); return d; } -LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { - return luaL_opt(L, luaL_checknumber, narg, def); +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, arg, def); } -LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) { - int isnum; - lua_Integer d = lua_tointegerx(L, narg, &isnum); - if (!isnum) - tag_error(L, narg, LUA_TNUMBER); - return d; +static void interror (lua_State *L, int arg) { + if (lua_isnumber(L, arg)) + luaL_argerror(L, arg, "number has no integer representation"); + else + tag_error(L, arg, LUA_TNUMBER); } -LUALIB_API lua_Unsigned luaL_checkunsigned (lua_State *L, int narg) { +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) { int isnum; - lua_Unsigned d = lua_tounsignedx(L, narg, &isnum); - if (!isnum) - tag_error(L, narg, LUA_TNUMBER); + lua_Integer d = lua_tointegerx(L, arg, &isnum); + if (!isnum) { + interror(L, arg); + } return d; } -LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg, +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg, lua_Integer def) { - return luaL_opt(L, luaL_checkinteger, narg, def); -} - - -LUALIB_API lua_Unsigned luaL_optunsigned (lua_State *L, int narg, - lua_Unsigned def) { - return luaL_opt(L, luaL_checkunsigned, narg, def); + return luaL_opt(L, luaL_checkinteger, arg, def); } /* }====================================================== */ @@ -424,6 +437,47 @@ LUALIB_API lua_Unsigned luaL_optunsigned (lua_State *L, int narg, ** ======================================================= */ +/* userdata to box arbitrary data */ +typedef struct UBox { + void *box; + size_t bsize; +} UBox; + + +static void *resizebox (lua_State *L, int idx, size_t newsize) { + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + UBox *box = (UBox *)lua_touserdata(L, idx); + void *temp = allocf(ud, box->box, box->bsize, newsize); + if (temp == NULL && newsize > 0) { /* allocation error? */ + resizebox(L, idx, 0); /* free buffer */ + luaL_error(L, "not enough memory for buffer allocation"); + } + box->box = temp; + box->bsize = newsize; + return temp; +} + + +static int boxgc (lua_State *L) { + resizebox(L, 1, 0); + return 0; +} + + +static void *newbox (lua_State *L, size_t newsize) { + UBox *box = (UBox *)lua_newuserdata(L, sizeof(UBox)); + box->box = NULL; + box->bsize = 0; + if (luaL_newmetatable(L, "LUABOX")) { /* creating metatable? */ + lua_pushcfunction(L, boxgc); + lua_setfield(L, -2, "__gc"); /* metatable.__gc = boxgc */ + } + lua_setmetatable(L, -2); + return resizebox(L, -1, newsize); +} + + /* ** check whether buffer is using a userdata on the stack as a temporary ** buffer @@ -444,11 +498,12 @@ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { if (newsize < B->n || newsize - B->n < sz) luaL_error(L, "buffer too large"); /* create larger buffer */ - newbuff = (char *)lua_newuserdata(L, newsize * sizeof(char)); - /* move content to new buffer */ - memcpy(newbuff, B->b, B->n * sizeof(char)); if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ + newbuff = (char *)resizebox(L, -1, newsize); + else { /* no buffer yet */ + newbuff = (char *)newbox(L, newsize); + memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ + } B->b = newbuff; B->size = newsize; } @@ -457,9 +512,11 @@ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { - char *b = luaL_prepbuffsize(B, l); - memcpy(b, s, l * sizeof(char)); - luaL_addsize(B, l); + if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ + char *b = luaL_prepbuffsize(B, l); + memcpy(b, s, l * sizeof(char)); + luaL_addsize(B, l); + } } @@ -471,8 +528,10 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { LUALIB_API void luaL_pushresult (luaL_Buffer *B) { lua_State *L = B->L; lua_pushlstring(L, B->b, B->n); - if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ + if (buffonstack(B)) { + resizebox(L, -2, 0); /* delete old buffer */ + lua_remove(L, -2); /* remove its header from the stack */ + } } @@ -523,7 +582,7 @@ LUALIB_API int luaL_ref (lua_State *L, int t) { int ref; if (lua_isnil(L, -1)) { lua_pop(L, 1); /* remove from stack */ - return LUA_REFNIL; /* `nil' has a unique fixed reference */ + return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } t = lua_absindex(L, t); lua_rawgeti(L, t, freelist); /* get first free element */ @@ -562,7 +621,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { typedef struct LoadF { int n; /* number of pre-read characters */ FILE *f; /* file being read */ - char buff[LUAL_BUFFERSIZE]; /* area for reading file */ + char buff[BUFSIZ]; /* area for reading file */ } LoadF; @@ -594,7 +653,7 @@ static int errfile (lua_State *L, const char *what, int fnameindex) { static int skipBOM (LoadF *lf) { - const char *p = "\xEF\xBB\xBF"; /* Utf8 BOM mark */ + const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ int c; lf->n = 0; do { @@ -654,7 +713,7 @@ static int luaL_loadfilex_raw (lua_State *L, const char *filename, const char *m readstatus = ferror(lf.f); if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ return errfile(L, "read", fnameindex); } lua_remove(L, fnameindex); @@ -697,23 +756,23 @@ LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { if (!lua_getmetatable(L, obj)) /* no metatable? */ - return 0; - lua_pushstring(L, event); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 2); /* remove metatable and metafield */ - return 0; - } + return LUA_TNIL; else { - lua_remove(L, -2); /* remove only metatable */ - return 1; + int tt; + lua_pushstring(L, event); + tt = lua_rawget(L, -2); + if (tt == LUA_TNIL) /* is metafield nil? */ + lua_pop(L, 2); /* remove metatable and metafield */ + else + lua_remove(L, -2); /* remove only metatable */ + return tt; /* return metafield type */ } } LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { obj = lua_absindex(L, obj); - if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */ return 0; lua_pushvalue(L, obj); lua_call(L, 1, 1); @@ -721,13 +780,13 @@ LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { } -LUALIB_API int luaL_len (lua_State *L, int idx) { - int l; +LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { + lua_Integer l; int isnum; lua_len(L, idx); - l = (int)lua_tointegerx(L, -1, &isnum); + l = lua_tointegerx(L, -1, &isnum); if (!isnum) - luaL_error(L, "object length is not a number"); + luaL_error(L, "object length is not an integer"); lua_pop(L, 1); /* remove object */ return l; } @@ -736,7 +795,13 @@ LUALIB_API int luaL_len (lua_State *L, int idx) { LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { if (!luaL_callmeta(L, idx, "__tostring")) { /* no metafield? */ switch (lua_type(L, idx)) { - case LUA_TNUMBER: + case LUA_TNUMBER: { + if (lua_isinteger(L, idx)) + lua_pushfstring(L, "%I", lua_tointeger(L, idx)); + else + lua_pushfstring(L, "%f", lua_tonumber(L, idx)); + break; + } case LUA_TSTRING: lua_pushvalue(L, idx); break; @@ -771,8 +836,7 @@ static const char *luaL_findtable (lua_State *L, int idx, e = strchr(fname, '.'); if (e == NULL) e = fname + strlen(fname); lua_pushlstring(L, fname, e - fname); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { /* no such field? */ + if (lua_rawget(L, -2) == LUA_TNIL) { /* no such field? */ lua_pop(L, 1); /* remove this nil */ lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ lua_pushlstring(L, fname, e - fname); @@ -809,13 +873,12 @@ static int libsize (const luaL_Reg *l) { LUALIB_API void luaL_pushmodule (lua_State *L, const char *modname, int sizehint) { luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); /* get _LOADED table */ - lua_getfield(L, -1, modname); /* get _LOADED[modname] */ - if (!lua_istable(L, -1)) { /* not found? */ + if (lua_getfield(L, -1, modname) != LUA_TTABLE) { /* no _LOADED[modname]? */ lua_pop(L, 1); /* remove previous result */ /* try global variable (and create one if it does not exist) */ lua_pushglobaltable(L); if (luaL_findtable(L, 0, modname, sizehint) != NULL) - luaL_error(L, "name conflict for module " LUA_QS, modname); + luaL_error(L, "name conflict for module '%s'", modname); lua_pushvalue(L, -1); lua_setfield(L, -3, modname); /* _LOADED[modname] = new table */ } @@ -845,7 +908,6 @@ LUALIB_API void luaL_openlib (lua_State *L, const char *libname, ** Returns with only the table at the stack. */ LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkversion(L); luaL_checkstack(L, nup, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ int i; @@ -863,8 +925,8 @@ LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { ** into the stack */ LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { - lua_getfield(L, idx, fname); - if (lua_istable(L, -1)) return 1; /* table already there */ + if (lua_getfield(L, idx, fname) == LUA_TTABLE) + return 1; /* table already there */ else { lua_pop(L, 1); /* remove previous result */ idx = lua_absindex(L, idx); @@ -877,22 +939,26 @@ LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { /* -** stripped-down 'require'. Calls 'openf' to open a module, -** registers the result in 'package.loaded' table and, if 'glb' -** is true, also registers the result in the global table. +** Stripped-down 'require': After checking "loaded" table, calls 'openf' +** to open a module, registers the result in 'package.loaded' table and, +** if 'glb' is true, also registers the result in the global table. ** Leaves resulting module on the top. */ LUALIB_API void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) { - lua_pushcfunction(L, openf); - lua_pushstring(L, modname); /* argument to open function */ - lua_call(L, 1, 1); /* open module */ luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); - lua_pushvalue(L, -2); /* make copy of module (call result) */ - lua_setfield(L, -2, modname); /* _LOADED[modname] = module */ - lua_pop(L, 1); /* remove _LOADED table */ + lua_getfield(L, -1, modname); /* _LOADED[modname] */ + if (!lua_toboolean(L, -1)) { /* package not already loaded? */ + lua_pop(L, 1); /* remove field */ + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); /* argument to open function */ + lua_call(L, 1, 1); /* call 'openf' to open module */ + lua_pushvalue(L, -1); /* make copy of module (call result) */ + lua_setfield(L, -3, modname); /* _LOADED[modname] = module */ + } + lua_remove(L, -2); /* remove _LOADED table */ if (glb) { - lua_pushvalue(L, -1); /* copy of 'mod' */ + lua_pushvalue(L, -1); /* copy of module */ lua_setglobal(L, modname); /* _G[modname] = module */ } } @@ -907,7 +973,7 @@ LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, while ((wild = strstr(s, p)) != NULL) { luaL_addlstring(&b, s, wild - s); /* push prefix */ luaL_addstring(&b, r); /* push replacement in place of pattern */ - s = wild + l; /* continue after `p' */ + s = wild + l; /* continue after 'p' */ } luaL_addstring(&b, s); /* push last suffix */ luaL_pushresult(&b); @@ -927,8 +993,8 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { static int panic (lua_State *L) { - luai_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", - lua_tostring(L, -1)); + lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", + lua_tostring(L, -1)); return 0; /* return to Lua to abort */ } @@ -940,20 +1006,15 @@ LUALIB_API lua_State *luaL_newstate (void) { } -LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver) { +LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { const lua_Number *v = lua_version(L); + if (sz != LUAL_NUMSIZES) /* check numeric types */ + luaL_error(L, "core and library have incompatible numeric types"); if (v != lua_version(NULL)) luaL_error(L, "multiple Lua VMs detected"); else if (*v != ver) luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", ver, *v); - /* check conversions number -> integer types */ - lua_pushnumber(L, -(lua_Number)0x1234); - if (lua_tointeger(L, -1) != -0x1234 || - lua_tounsigned(L, -1) != (lua_Unsigned)-0x1234) - luaL_error(L, "bad conversion number->int;" - " must recompile Lua with proper settings"); - lua_pop(L, 1); } //added by xdczju@sina.com @@ -989,7 +1050,7 @@ static shared_lstate_t shared_closure[] = { static atomic_t shared_lstate_count = 0; static shared_lstate_t* get_shared_lstate() { - int i; + size_t i; shared_lstate_t* sl; for (;;) { for (i = 1; i < array_size(shared_closure); ++i) { @@ -1129,9 +1190,9 @@ static int luaL_loadfilex_clone (lua_State *L, const char *filename, const char return LUA_OK; } -/* need to wait for all threads to be terminated */ +/* warning: need to wait for all threads to be terminated in muti-thread situation */ LUALIB_API int luaL_closesharedclosure() { - int i; + size_t i; shared_lstate_t* sl; clear_shared_closure(); @@ -1141,13 +1202,13 @@ LUALIB_API int luaL_closesharedclosure() { lua_close(sl->L); } } - //TO BE FIXED!!! MUTI-THREAD PROBLEM! - printf("shared_lstate_count %ld\n", shared_lstate_count); + //printf("shared_lstate_count %ld\n", shared_lstate_count); return 0; } static int cache_clear(lua_State *L) { + (void)L; /* not used */ clear_shared_closure(); return 0; } diff --git a/deps/lua/lauxlib.h b/deps/lua/lauxlib.h index eef6e06..0c9f0e1 100644 --- a/deps/lua/lauxlib.h +++ b/deps/lua/lauxlib.h @@ -1,5 +1,5 @@ /* -** $Id: lauxlib.h,v 1.120.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lauxlib.h,v 1.129 2015/11/23 11:29:43 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ @@ -16,7 +16,7 @@ -/* extra error code for `luaL_load' */ +/* extra error code for 'luaL_load' */ #define LUA_ERRFILE (LUA_ERRERR+1) @@ -26,30 +26,30 @@ typedef struct luaL_Reg { } luaL_Reg; -LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver); -#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM) +#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) + +LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); +#define luaL_checkversion(L) \ + luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); -LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); -LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, +LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, size_t *l); -LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, const char *def, size_t *l); -LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); -LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); -LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); -LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, lua_Integer def); -LUALIB_API lua_Unsigned (luaL_checkunsigned) (lua_State *L, int numArg); -LUALIB_API lua_Unsigned (luaL_optunsigned) (lua_State *L, int numArg, - lua_Unsigned def); LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); -LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); -LUALIB_API void (luaL_checkany) (lua_State *L, int narg); +LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int arg); LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); @@ -59,13 +59,13 @@ LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); LUALIB_API void (luaL_where) (lua_State *L, int lvl); LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); -LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, +LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, const char *const lst[]); LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); LUALIB_API int (luaL_execresult) (lua_State *L, int stat); -/* pre-defined references */ +/* predefined references */ #define LUA_NOREF (-2) #define LUA_REFNIL (-1) @@ -85,7 +85,7 @@ LUALIB_API lua_State *(luaL_newstate) (void); LUALIB_API lua_State *(luaL_newstateex) (void* ud); -LUALIB_API int (luaL_len) (lua_State *L, int idx); +LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, const char *r); @@ -110,16 +110,13 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, #define luaL_newlibtable(L,l) \ lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) -#define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) +#define luaL_newlib(L,l) \ + (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) -#define luaL_argcheck(L, cond,numarg,extramsg) \ - ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_argcheck(L, cond,arg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (arg), (extramsg)))) #define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) #define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) -#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) -#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) -#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) -#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) #define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) @@ -208,6 +205,53 @@ LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, #endif + +/* +** {================================================================== +** "Abstraction Layer" for basic report of messages and errors +** =================================================================== +*/ + +/* print a string */ +#if !defined(lua_writestring) +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif + +/* print a newline and flush the output */ +#if !defined(lua_writeline) +#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) +#endif + +/* print an error message */ +#if !defined(lua_writestringerror) +#define lua_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) +#endif + +/* }================================================================== */ + + +/* +** {============================================================ +** Compatibility with deprecated conversions +** ============================================================= +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) +#define luaL_optunsigned(L,a,d) \ + ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) + +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) + +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#endif +/* }============================================================ */ + + #if defined(LUA_USE_SHARED_PROTO) LUALIB_API int (luaL_closesharedclosure) (); #endif diff --git a/deps/lua/lbaselib.c b/deps/lua/lbaselib.c index 5255b3c..861823d 100644 --- a/deps/lua/lbaselib.c +++ b/deps/lua/lbaselib.c @@ -1,9 +1,13 @@ /* -** $Id: lbaselib.c,v 1.276.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lbaselib.c,v 1.312 2015/10/29 15:21:04 roberto Exp $ ** Basic library ** See Copyright Notice in lua.h */ +#define lbaselib_c +#define LUA_LIB + +#include "lprefix.h" #include @@ -11,9 +15,6 @@ #include #include -#define lbaselib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -32,62 +33,74 @@ static int luaB_print (lua_State *L) { lua_call(L, 1, 1); s = lua_tolstring(L, -1, &l); /* get result */ if (s == NULL) - return luaL_error(L, - LUA_QL("tostring") " must return a string to " LUA_QL("print")); - if (i>1) luai_writestring("\t", 1); - luai_writestring(s, l); + return luaL_error(L, "'tostring' must return a string to 'print'"); + if (i>1) lua_writestring("\t", 1); + lua_writestring(s, l); lua_pop(L, 1); /* pop result */ } - luai_writeline(); + lua_writeline(); return 0; } #define SPACECHARS " \f\n\r\t\v" +static const char *b_str2int (const char *s, int base, lua_Integer *pn) { + lua_Unsigned n = 0; + int neg = 0; + s += strspn(s, SPACECHARS); /* skip initial spaces */ + if (*s == '-') { s++; neg = 1; } /* handle signal */ + else if (*s == '+') s++; + if (!isalnum((unsigned char)*s)) /* no digit? */ + return NULL; + do { + int digit = (isdigit((unsigned char)*s)) ? *s - '0' + : (toupper((unsigned char)*s) - 'A') + 10; + if (digit >= base) return NULL; /* invalid numeral */ + n = n * base + digit; + s++; + } while (isalnum((unsigned char)*s)); + s += strspn(s, SPACECHARS); /* skip trailing spaces */ + *pn = (lua_Integer)((neg) ? (0u - n) : n); + return s; +} + + static int luaB_tonumber (lua_State *L) { - if (lua_isnoneornil(L, 2)) { /* standard conversion */ - int isnum; - lua_Number n = lua_tonumberx(L, 1, &isnum); - if (isnum) { - lua_pushnumber(L, n); - return 1; - } /* else not a number; must be something */ + if (lua_isnoneornil(L, 2)) { /* standard conversion? */ luaL_checkany(L, 1); + if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */ + lua_settop(L, 1); /* yes; return it */ + return 1; + } + else { + size_t l; + const char *s = lua_tolstring(L, 1, &l); + if (s != NULL && lua_stringtonumber(L, s) == l + 1) + return 1; /* successful conversion to number */ + /* else not a number */ + } } else { size_t l; - const char *s = luaL_checklstring(L, 1, &l); - const char *e = s + l; /* end point for 's' */ - int base = luaL_checkint(L, 2); - int neg = 0; + const char *s; + lua_Integer n = 0; /* to avoid warnings */ + lua_Integer base = luaL_checkinteger(L, 2); + luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ + s = lua_tolstring(L, 1, &l); luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - s += strspn(s, SPACECHARS); /* skip initial spaces */ - if (*s == '-') { s++; neg = 1; } /* handle signal */ - else if (*s == '+') s++; - if (isalnum((unsigned char)*s)) { - lua_Number n = 0; - do { - int digit = (isdigit((unsigned char)*s)) ? *s - '0' - : toupper((unsigned char)*s) - 'A' + 10; - if (digit >= base) break; /* invalid numeral; force a fail */ - n = n * (lua_Number)base + (lua_Number)digit; - s++; - } while (isalnum((unsigned char)*s)); - s += strspn(s, SPACECHARS); /* skip trailing spaces */ - if (s == e) { /* no invalid trailing characters? */ - lua_pushnumber(L, (neg) ? -n : n); - return 1; - } /* else not a number */ + if (b_str2int(s, (int)base, &n) == s + l) { + lua_pushinteger(L, n); + return 1; } /* else not a number */ - } + } /* else not a number */ lua_pushnil(L); /* not a number */ return 1; } static int luaB_error (lua_State *L) { - int level = luaL_optint(L, 2, 1); + int level = (int)luaL_optinteger(L, 2, 1); lua_settop(L, 1); if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ luaL_where(L, level); @@ -114,7 +127,7 @@ static int luaB_setmetatable (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table expected"); - if (luaL_getmetafield(L, 1, "__metatable")) + if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL) return luaL_error(L, "cannot change a protected metatable"); lua_settop(L, 2); lua_setmetatable(L, 1); @@ -160,19 +173,18 @@ static int luaB_rawset (lua_State *L) { static int luaB_collectgarbage (lua_State *L) { static const char *const opts[] = {"stop", "restart", "collect", "count", "step", "setpause", "setstepmul", - "setmajorinc", "isrunning", "generational", "incremental", NULL}; + "isrunning", NULL}; static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, - LUA_GCSETMAJORINC, LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC}; + LUA_GCISRUNNING}; int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; - int ex = luaL_optint(L, 2, 0); + int ex = (int)luaL_optinteger(L, 2, 0); int res = lua_gc(L, o, ex); switch (o) { case LUA_GCCOUNT: { int b = lua_gc(L, LUA_GCCOUNTB, 0); - lua_pushnumber(L, res + ((lua_Number)b/1024)); - lua_pushinteger(L, b); - return 2; + lua_pushnumber(L, (lua_Number)res + ((lua_Number)b/1024)); + return 1; } case LUA_GCSTEP: case LUA_GCISRUNNING: { lua_pushboolean(L, res); @@ -187,15 +199,16 @@ static int luaB_collectgarbage (lua_State *L) { static int luaB_type (lua_State *L) { - luaL_checkany(L, 1); - lua_pushstring(L, luaL_typename(L, 1)); + int t = lua_type(L, 1); + luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); + lua_pushstring(L, lua_typename(L, t)); return 1; } static int pairsmeta (lua_State *L, const char *method, int iszero, lua_CFunction iter) { - if (!luaL_getmetafield(L, 1, method)) { /* no metamethod? */ + if (luaL_getmetafield(L, 1, method) == LUA_TNIL) { /* no metamethod? */ luaL_checktype(L, 1, LUA_TTABLE); /* argument must be a table */ lua_pushcfunction(L, iter); /* will return generator, */ lua_pushvalue(L, 1); /* state, */ @@ -227,18 +240,31 @@ static int luaB_pairs (lua_State *L) { } +/* +** Traversal function for 'ipairs' +*/ static int ipairsaux (lua_State *L) { - int i = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - i++; /* next value */ + lua_Integer i = luaL_checkinteger(L, 2) + 1; lua_pushinteger(L, i); - lua_rawgeti(L, 1, i); - return (lua_isnil(L, -1)) ? 1 : 2; + return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; } +/* +** This function will use either 'ipairsaux' or 'ipairsaux_raw' to +** traverse a table, depending on whether the table has metamethods +** that can affect the traversal. +*/ static int luaB_ipairs (lua_State *L) { +#if defined(LUA_COMPAT_IPAIRS) return pairsmeta(L, "__ipairs", 1, ipairsaux); +#else + luaL_checkany(L, 1); + lua_pushcfunction(L, ipairsaux); /* iteration function */ + lua_pushvalue(L, 1); /* state */ + lua_pushinteger(L, 0); /* initial value */ + return 3; +#endif } @@ -284,7 +310,7 @@ static int luaB_loadfile (lua_State *L) { /* -** Reader for generic `load' function: `lua_load' uses the +** Reader for generic 'load' function: 'lua_load' uses the ** stack for internal stuff, so the reader cannot change the ** stack top. Instead, it keeps its resulting string in a ** reserved slot inside the stack. @@ -328,7 +354,8 @@ static int luaB_load (lua_State *L) { /* }====================================================== */ -static int dofilecont (lua_State *L) { +static int dofilecont (lua_State *L, int d1, lua_KContext d2) { + (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */ return lua_gettop(L) - 1; } @@ -339,14 +366,20 @@ static int luaB_dofile (lua_State *L) { if (luaL_loadfile(L, fname) != LUA_OK) return lua_error(L); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); - return dofilecont(L); + return dofilecont(L, 0, 0); } static int luaB_assert (lua_State *L) { - if (!lua_toboolean(L, 1)) - return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); - return lua_gettop(L); + if (lua_toboolean(L, 1)) /* condition is true? */ + return lua_gettop(L); /* return all arguments */ + else { /* error */ + luaL_checkany(L, 1); /* there must be a condition */ + lua_remove(L, 1); /* remove it */ + lua_pushliteral(L, "assertion failed!"); /* default message */ + lua_settop(L, 1); /* leave only message (default if no other one) */ + return luaB_error(L); /* call 'error' */ + } } @@ -357,53 +390,57 @@ static int luaB_select (lua_State *L) { return 1; } else { - int i = luaL_checkint(L, 1); + lua_Integer i = luaL_checkinteger(L, 1); if (i < 0) i = n + i; else if (i > n) i = n; luaL_argcheck(L, 1 <= i, 1, "index out of range"); - return n - i; + return n - (int)i; } } -static int finishpcall (lua_State *L, int status) { - if (!lua_checkstack(L, 1)) { /* no space for extra boolean? */ - lua_settop(L, 0); /* create space for return values */ - lua_pushboolean(L, 0); - lua_pushstring(L, "stack overflow"); +/* +** Continuation function for 'pcall' and 'xpcall'. Both functions +** already pushed a 'true' before doing the call, so in case of success +** 'finishpcall' only has to return everything in the stack minus +** 'extra' values (where 'extra' is exactly the number of items to be +** ignored). +*/ +static int finishpcall (lua_State *L, int status, lua_KContext extra) { + if (status != LUA_OK && status != LUA_YIELD) { /* error? */ + lua_pushboolean(L, 0); /* first result (false) */ + lua_pushvalue(L, -2); /* error message */ return 2; /* return false, msg */ } - lua_pushboolean(L, status); /* first result (status) */ - lua_replace(L, 1); /* put first result in first slot */ - return lua_gettop(L); -} - - -static int pcallcont (lua_State *L) { - int status = lua_getctx(L, NULL); - return finishpcall(L, (status == LUA_YIELD)); + else + return lua_gettop(L) - (int)extra; /* return all results */ } static int luaB_pcall (lua_State *L) { int status; luaL_checkany(L, 1); - lua_pushnil(L); - lua_insert(L, 1); /* create space for status result */ - status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, pcallcont); - return finishpcall(L, (status == LUA_OK)); + lua_pushboolean(L, 1); /* first result if no errors */ + lua_insert(L, 1); /* put it in place */ + status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall); + return finishpcall(L, status, 0); } +/* +** Do a protected call with error handling. After 'lua_rotate', the +** stack will have ; so, the function passes +** 2 to 'finishpcall' to skip the 2 first values when returning results. +*/ static int luaB_xpcall (lua_State *L) { int status; int n = lua_gettop(L); - luaL_argcheck(L, n >= 2, 2, "value expected"); - lua_pushvalue(L, 1); /* exchange function... */ - lua_copy(L, 2, 1); /* ...and error handler */ - lua_replace(L, 2); - status = lua_pcallk(L, n - 2, LUA_MULTRET, 1, 0, pcallcont); - return finishpcall(L, (status == LUA_OK)); + luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */ + lua_pushboolean(L, 1); /* first result */ + lua_pushvalue(L, 1); /* function */ + lua_rotate(L, 3, 2); /* move them below function's arguments */ + status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall); + return finishpcall(L, status, 2); } @@ -440,19 +477,23 @@ static const luaL_Reg base_funcs[] = { {"tostring", luaB_tostring}, {"type", luaB_type}, {"xpcall", luaB_xpcall}, + /* placeholders */ + {"_G", NULL}, + {"_VERSION", NULL}, {NULL, NULL} }; LUAMOD_API int luaopen_base (lua_State *L) { - /* set global _G */ - lua_pushglobaltable(L); - lua_pushglobaltable(L); - lua_setfield(L, -2, "_G"); /* open lib into global table */ + lua_pushglobaltable(L); luaL_setfuncs(L, base_funcs, 0); + /* set global _G */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_G"); + /* set global _VERSION */ lua_pushliteral(L, LUA_VERSION); - lua_setfield(L, -2, "_VERSION"); /* set global _VERSION */ + lua_setfield(L, -2, "_VERSION"); return 1; } diff --git a/deps/lua/lbitlib.c b/deps/lua/lbitlib.c index 31c7b66..1cb1d5b 100644 --- a/deps/lua/lbitlib.c +++ b/deps/lua/lbitlib.c @@ -1,5 +1,5 @@ /* -** $Id: lbitlib.c,v 1.18.1.2 2013/07/09 18:01:41 roberto Exp $ +** $Id: lbitlib.c,v 1.30 2015/11/11 19:08:09 roberto Exp $ ** Standard library for bitwise operations ** See Copyright Notice in lua.h */ @@ -7,20 +7,36 @@ #define lbitlib_c #define LUA_LIB +#include "lprefix.h" + + #include "lua.h" #include "lauxlib.h" #include "lualib.h" +#if defined(LUA_COMPAT_BITLIB) /* { */ + + +#define pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) +#define checkunsigned(L,i) ((lua_Unsigned)luaL_checkinteger(L,i)) + + /* number of bits to consider in a number */ #if !defined(LUA_NBITS) #define LUA_NBITS 32 #endif +/* +** a lua_Unsigned with its first LUA_NBITS bits equal to 1. (Shift must +** be made in two parts to avoid problems when LUA_NBITS is equal to the +** number of bits in a lua_Unsigned.) +*/ #define ALLONES (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1)) + /* macro to trim extra bits */ #define trim(x) ((x) & ALLONES) @@ -29,28 +45,25 @@ #define mask(n) (~((ALLONES << 1) << ((n) - 1))) -typedef lua_Unsigned b_uint; - - -static b_uint andaux (lua_State *L) { +static lua_Unsigned andaux (lua_State *L) { int i, n = lua_gettop(L); - b_uint r = ~(b_uint)0; + lua_Unsigned r = ~(lua_Unsigned)0; for (i = 1; i <= n; i++) - r &= luaL_checkunsigned(L, i); + r &= checkunsigned(L, i); return trim(r); } static int b_and (lua_State *L) { - b_uint r = andaux(L); - lua_pushunsigned(L, r); + lua_Unsigned r = andaux(L); + pushunsigned(L, r); return 1; } static int b_test (lua_State *L) { - b_uint r = andaux(L); + lua_Unsigned r = andaux(L); lua_pushboolean(L, r != 0); return 1; } @@ -58,32 +71,32 @@ static int b_test (lua_State *L) { static int b_or (lua_State *L) { int i, n = lua_gettop(L); - b_uint r = 0; + lua_Unsigned r = 0; for (i = 1; i <= n; i++) - r |= luaL_checkunsigned(L, i); - lua_pushunsigned(L, trim(r)); + r |= checkunsigned(L, i); + pushunsigned(L, trim(r)); return 1; } static int b_xor (lua_State *L) { int i, n = lua_gettop(L); - b_uint r = 0; + lua_Unsigned r = 0; for (i = 1; i <= n; i++) - r ^= luaL_checkunsigned(L, i); - lua_pushunsigned(L, trim(r)); + r ^= checkunsigned(L, i); + pushunsigned(L, trim(r)); return 1; } static int b_not (lua_State *L) { - b_uint r = ~luaL_checkunsigned(L, 1); - lua_pushunsigned(L, trim(r)); + lua_Unsigned r = ~checkunsigned(L, 1); + pushunsigned(L, trim(r)); return 1; } -static int b_shift (lua_State *L, b_uint r, int i) { +static int b_shift (lua_State *L, lua_Unsigned r, lua_Integer i) { if (i < 0) { /* shift right? */ i = -i; r = trim(r); @@ -95,54 +108,54 @@ static int b_shift (lua_State *L, b_uint r, int i) { else r <<= i; r = trim(r); } - lua_pushunsigned(L, r); + pushunsigned(L, r); return 1; } static int b_lshift (lua_State *L) { - return b_shift(L, luaL_checkunsigned(L, 1), luaL_checkint(L, 2)); + return b_shift(L, checkunsigned(L, 1), luaL_checkinteger(L, 2)); } static int b_rshift (lua_State *L) { - return b_shift(L, luaL_checkunsigned(L, 1), -luaL_checkint(L, 2)); + return b_shift(L, checkunsigned(L, 1), -luaL_checkinteger(L, 2)); } static int b_arshift (lua_State *L) { - b_uint r = luaL_checkunsigned(L, 1); - int i = luaL_checkint(L, 2); - if (i < 0 || !(r & ((b_uint)1 << (LUA_NBITS - 1)))) + lua_Unsigned r = checkunsigned(L, 1); + lua_Integer i = luaL_checkinteger(L, 2); + if (i < 0 || !(r & ((lua_Unsigned)1 << (LUA_NBITS - 1)))) return b_shift(L, r, -i); else { /* arithmetic shift for 'negative' number */ if (i >= LUA_NBITS) r = ALLONES; else - r = trim((r >> i) | ~(~(b_uint)0 >> i)); /* add signal bit */ - lua_pushunsigned(L, r); + r = trim((r >> i) | ~(trim(~(lua_Unsigned)0) >> i)); /* add signal bit */ + pushunsigned(L, r); return 1; } } -static int b_rot (lua_State *L, int i) { - b_uint r = luaL_checkunsigned(L, 1); - i &= (LUA_NBITS - 1); /* i = i % NBITS */ +static int b_rot (lua_State *L, lua_Integer d) { + lua_Unsigned r = checkunsigned(L, 1); + int i = d & (LUA_NBITS - 1); /* i = d % NBITS */ r = trim(r); if (i != 0) /* avoid undefined shift of LUA_NBITS when i == 0 */ r = (r << i) | (r >> (LUA_NBITS - i)); - lua_pushunsigned(L, trim(r)); + pushunsigned(L, trim(r)); return 1; } static int b_lrot (lua_State *L) { - return b_rot(L, luaL_checkint(L, 2)); + return b_rot(L, luaL_checkinteger(L, 2)); } static int b_rrot (lua_State *L) { - return b_rot(L, -luaL_checkint(L, 2)); + return b_rot(L, -luaL_checkinteger(L, 2)); } @@ -153,36 +166,35 @@ static int b_rrot (lua_State *L) { ** 'width' being used uninitialized.) */ static int fieldargs (lua_State *L, int farg, int *width) { - int f = luaL_checkint(L, farg); - int w = luaL_optint(L, farg + 1, 1); + lua_Integer f = luaL_checkinteger(L, farg); + lua_Integer w = luaL_optinteger(L, farg + 1, 1); luaL_argcheck(L, 0 <= f, farg, "field cannot be negative"); luaL_argcheck(L, 0 < w, farg + 1, "width must be positive"); if (f + w > LUA_NBITS) luaL_error(L, "trying to access non-existent bits"); - *width = w; - return f; + *width = (int)w; + return (int)f; } static int b_extract (lua_State *L) { int w; - b_uint r = luaL_checkunsigned(L, 1); + lua_Unsigned r = trim(checkunsigned(L, 1)); int f = fieldargs(L, 2, &w); r = (r >> f) & mask(w); - lua_pushunsigned(L, r); + pushunsigned(L, r); return 1; } static int b_replace (lua_State *L) { int w; - b_uint r = luaL_checkunsigned(L, 1); - b_uint v = luaL_checkunsigned(L, 2); + lua_Unsigned r = trim(checkunsigned(L, 1)); + lua_Unsigned v = trim(checkunsigned(L, 2)); int f = fieldargs(L, 3, &w); - int m = mask(w); - v &= m; /* erase bits outside given width */ - r = (r & ~(m << f)) | (v << f); - lua_pushunsigned(L, r); + lua_Unsigned m = mask(w); + r = (r & ~(m << f)) | ((v & m) << f); + pushunsigned(L, r); return 1; } @@ -210,3 +222,12 @@ LUAMOD_API int luaopen_bit32 (lua_State *L) { return 1; } + +#else /* }{ */ + + +LUAMOD_API int luaopen_bit32 (lua_State *L) { + return luaL_error(L, "library 'bit32' has been deprecated"); +} + +#endif /* } */ diff --git a/deps/lua/lcode.c b/deps/lua/lcode.c index 5ba570e..e86b1ca 100644 --- a/deps/lua/lcode.c +++ b/deps/lua/lcode.c @@ -1,15 +1,18 @@ /* -** $Id: lcode.c,v 2.62.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lcode.c,v 2.103 2015/11/19 19:16:22 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ - -#include - #define lcode_c #define LUA_CORE +#include "lprefix.h" + + +#include +#include + #include "lua.h" #include "lcode.h" @@ -26,11 +29,25 @@ #include "lvm.h" +/* Maximum number of registers in a Lua function (must fit in 8 bits) */ +#define MAXREGS 255 + + #define hasjumps(e) ((e)->t != (e)->f) -static int isnumeral(expdesc *e) { - return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); +static int tonumeral(expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a numeral */ + switch (e->k) { + case VKINT: + if (v) setivalue(v, e->u.ival); + return 1; + case VKFLT: + if (v) setfltvalue(v, e->u.nval); + return 1; + default: return 0; + } } @@ -88,7 +105,7 @@ static void fixjump (FuncState *fs, int pc, int dest) { /* -** returns current `pc' and marks it as a jump target (to avoid wrong +** returns current 'pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). */ int luaK_getlabel (FuncState *fs) { @@ -176,7 +193,7 @@ void luaK_patchlist (FuncState *fs, int list, int target) { } -LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level) { +void luaK_patchclose (FuncState *fs, int list, int level) { level++; /* argument is +1 to reserve 0 as non-op */ while (list != NO_JUMP) { int next = getjump(fs, list); @@ -211,7 +228,7 @@ void luaK_concat (FuncState *fs, int *l1, int l2) { static int luaK_code (FuncState *fs, Instruction i) { SharedProto *f = fs->f->sp; - dischargejpc(fs); /* `pc' will change */ + dischargejpc(fs); /* 'pc' will change */ /* put new instruction in code array */ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, MAX_INT, "opcodes"); @@ -261,8 +278,9 @@ int luaK_codek (FuncState *fs, int reg, int k) { void luaK_checkstack (FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->sp->maxstacksize) { - if (newstack >= MAXSTACK) - luaX_syntaxerror(fs->ls, "function or expression too complex"); + if (newstack >= MAXREGS) + luaX_syntaxerror(fs->ls, + "function or expression needs too many registers"); fs->f->sp->maxstacksize = cast_byte(newstack); } } @@ -288,25 +306,28 @@ static void freeexp (FuncState *fs, expdesc *e) { } +/* +** Use scanner's table to cache position of constants in constant list +** and try to reuse constants +*/ static int addk (FuncState *fs, TValue *key, TValue *v) { lua_State *L = fs->ls->L; - TValue *idx = luaH_set(L, fs->h, key); Proto *f = fs->f; + TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */ int k, oldsize; - if (ttisnumber(idx)) { - lua_Number n = nvalue(idx); - lua_number2int(k, n); - if (luaV_rawequalobj(&f->k[k], v)) - return k; - /* else may be a collision (e.g., between 0.0 and "\0\0\0\0\0\0\0\0"); - go through and create a new entry for this value */ + if (ttisinteger(idx)) { /* is there an index there? */ + k = cast_int(ivalue(idx)); + /* correct value? (warning: must distinguish floats from integers!) */ + if (k < fs->nk && ttype(&f->k[k]) == ttype(v) && + luaV_rawequalobj(&f->k[k], v)) + return k; /* reuse index */ } /* constant not found; create a new entry */ oldsize = f->sp->sizek; k = fs->nk; /* numerical value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ - setnvalue(idx, cast_num(k)); + setivalue(idx, k); luaM_growvector(L, f->k, k, f->sp->sizek, TValue, MAXARG_Ax, "constants"); while (oldsize < f->sp->sizek) setnilvalue(&f->k[oldsize++]); setobj(L, &f->k[k], v); @@ -323,20 +344,23 @@ int luaK_stringK (FuncState *fs, TString *s) { } -int luaK_numberK (FuncState *fs, lua_Number r) { - int n; - lua_State *L = fs->ls->L; +/* +** Integers use userdata as keys to avoid collision with floats with same +** value; conversion to 'void*' used only for hashing, no "precision" +** problems +*/ +int luaK_intK (FuncState *fs, lua_Integer n) { + TValue k, o; + setpvalue(&k, cast(void*, cast(size_t, n))); + setivalue(&o, n); + return addk(fs, &k, &o); +} + + +static int luaK_numberK (FuncState *fs, lua_Number r) { TValue o; - setnvalue(&o, r); - if (r == 0 || luai_numisnan(NULL, r)) { /* handle -0 and NaN */ - /* use raw representation as key to avoid numeric problems */ - setsvalue(L, L->top++, luaS_newlstr(L, (char *)&r, sizeof(r))); - n = addk(fs, L->top - 1, &o); - L->top--; - } - else - n = addk(fs, &o, &o); /* regular case */ - return n; + setfltvalue(&o, r); + return addk(fs, &o, &o); } @@ -351,7 +375,7 @@ static int nilK (FuncState *fs) { TValue k, v; setnilvalue(&v); /* cannot use nil as key; instead use table itself to represent nil */ - sethvalue(fs->ls->L, &k, fs->h); + sethvalue(fs->ls->L, &k, fs->ls->h); return addk(fs, &k, &v); } @@ -433,10 +457,14 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_codek(fs, reg, e->u.info); break; } - case VKNUM: { + case VKFLT: { luaK_codek(fs, reg, luaK_numberK(fs, e->u.nval)); break; } + case VKINT: { + luaK_codek(fs, reg, luaK_intK(fs, e->u.ival)); + break; + } case VRELOCABLE: { Instruction *pc = &getcode(fs, e); SETARG_A(*pc, reg); @@ -468,7 +496,7 @@ static void discharge2anyreg (FuncState *fs, expdesc *e) { static void exp2reg (FuncState *fs, expdesc *e, int reg) { discharge2reg(fs, e, reg); if (e->k == VJMP) - luaK_concat(fs, &e->t, e->u.info); /* put this jump in `t' list */ + luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ if (hasjumps(e)) { int final; /* position after whole expression */ int p_f = NO_JUMP; /* position of an eventual LOAD false */ @@ -538,13 +566,19 @@ int luaK_exp2RK (FuncState *fs, expdesc *e) { } else break; } - case VKNUM: { + case VKINT: { + e->u.info = luaK_intK(fs, e->u.ival); + e->k = VK; + goto vk; + } + case VKFLT: { e->u.info = luaK_numberK(fs, e->u.nval); e->k = VK; - /* go through */ } + /* FALLTHROUGH */ case VK: { - if (e->u.info <= MAXINDEXRK) /* constant fits in argC? */ + vk: + if (e->u.info <= MAXINDEXRK) /* constant fits in 'argC'? */ return RKASK(e->u.info); else break; } @@ -627,7 +661,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) { pc = e->u.info; break; } - case VK: case VKNUM: case VTRUE: { + case VK: case VKFLT: case VKINT: case VTRUE: { pc = NO_JUMP; /* always true; do nothing */ break; } @@ -636,7 +670,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) { break; } } - luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ + luaK_concat(fs, &e->f, pc); /* insert last jump in 'f' list */ luaK_patchtohere(fs, e->t); e->t = NO_JUMP; } @@ -659,7 +693,7 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) { break; } } - luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ + luaK_concat(fs, &e->t, pc); /* insert last jump in 't' list */ luaK_patchtohere(fs, e->f); e->f = NO_JUMP; } @@ -672,7 +706,7 @@ static void codenot (FuncState *fs, expdesc *e) { e->k = VTRUE; break; } - case VK: case VKNUM: case VTRUE: { + case VK: case VKFLT: case VKINT: case VTRUE: { e->k = VFALSE; break; } @@ -710,25 +744,70 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { } -static int constfolding (OpCode op, expdesc *e1, expdesc *e2) { - lua_Number r; - if (!isnumeral(e1) || !isnumeral(e2)) return 0; - if ((op == OP_DIV || op == OP_MOD) && e2->u.nval == 0) - return 0; /* do not attempt to divide by 0 */ - r = luaO_arith(op - OP_ADD + LUA_OPADD, e1->u.nval, e2->u.nval); - e1->u.nval = r; +/* +** return false if folding can raise an error +*/ +static int validop (int op, TValue *v1, TValue *v2) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */ + lua_Integer i; + return (tointeger(v1, &i) && tointeger(v2, &i)); + } + case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */ + return (nvalue(v2) != 0); + default: return 1; /* everything else is valid */ + } +} + + +/* +** Try to "constant-fold" an operation; return 1 iff successful +*/ +static int constfolding (FuncState *fs, int op, expdesc *e1, expdesc *e2) { + TValue v1, v2, res; + if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) + return 0; /* non-numeric operands or not safe to fold */ + luaO_arith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ + if (ttisinteger(&res)) { + e1->k = VKINT; + e1->u.ival = ivalue(&res); + } + else { /* folds neither NaN nor 0.0 (to avoid collapsing with -0.0) */ + lua_Number n = fltvalue(&res); + if (luai_numisnan(n) || n == 0) + return 0; + e1->k = VKFLT; + e1->u.nval = n; + } return 1; } -static void codearith (FuncState *fs, OpCode op, - expdesc *e1, expdesc *e2, int line) { - if (constfolding(op, e1, e2)) - return; +/* +** Code for binary and unary expressions that "produce values" +** (arithmetic operations, bitwise operations, concat, length). First +** try to do constant folding (only for numeric [arithmetic and +** bitwise] operations, which is what 'lua_arith' accepts). +** Expression to produce final result will be encoded in 'e1'. +*/ +static void codeexpval (FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int line) { + lua_assert(op >= OP_ADD); + if (op <= OP_BNOT && constfolding(fs, (op - OP_ADD) + LUA_OPADD, e1, e2)) + return; /* result has been folded */ else { - int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; - int o1 = luaK_exp2RK(fs, e1); - if (o1 > o2) { + int o1, o2; + /* move operands to registers (if needed) */ + if (op == OP_UNM || op == OP_BNOT || op == OP_LEN) { /* unary op? */ + o2 = 0; /* no second expression */ + o1 = luaK_exp2anyreg(fs, e1); /* cannot operate on constants */ + } + else { /* regular case (binary operators) */ + o2 = luaK_exp2RK(fs, e2); /* both operands are "RK" */ + o1 = luaK_exp2RK(fs, e1); + } + if (o1 > o2) { /* free registers in proper order */ freeexp(fs, e1); freeexp(fs, e2); } @@ -736,8 +815,8 @@ static void codearith (FuncState *fs, OpCode op, freeexp(fs, e2); freeexp(fs, e1); } - e1->u.info = luaK_codeABC(fs, op, 0, o1, o2); - e1->k = VRELOCABLE; + e1->u.info = luaK_codeABC(fs, op, 0, o1, o2); /* generate opcode */ + e1->k = VRELOCABLE; /* all those operations are relocatable */ luaK_fixline(fs, line); } } @@ -750,7 +829,7 @@ static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, freeexp(fs, e2); freeexp(fs, e1); if (cond == 0 && op != OP_EQ) { - int temp; /* exchange args to replace by `<' or `<=' */ + int temp; /* exchange args to replace by '<' or '<=' */ temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ cond = 1; } @@ -761,23 +840,13 @@ static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { expdesc e2; - e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; + e2.t = e2.f = NO_JUMP; e2.k = VKINT; e2.u.ival = 0; switch (op) { - case OPR_MINUS: { - if (isnumeral(e)) /* minus constant? */ - e->u.nval = luai_numunm(NULL, e->u.nval); /* fold it */ - else { - luaK_exp2anyreg(fs, e); - codearith(fs, OP_UNM, e, &e2, line); - } + case OPR_MINUS: case OPR_BNOT: case OPR_LEN: { + codeexpval(fs, cast(OpCode, (op - OPR_MINUS) + OP_UNM), e, &e2, line); break; } case OPR_NOT: codenot(fs, e); break; - case OPR_LEN: { - luaK_exp2anyreg(fs, e); /* cannot operate on constants */ - codearith(fs, OP_LEN, e, &e2, line); - break; - } default: lua_assert(0); } } @@ -794,12 +863,15 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { break; } case OPR_CONCAT: { - luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ + luaK_exp2nextreg(fs, v); /* operand must be on the 'stack' */ break; } - case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: - case OPR_MOD: case OPR_POW: { - if (!isnumeral(v)) luaK_exp2RK(fs, v); + case OPR_ADD: case OPR_SUB: + case OPR_MUL: case OPR_DIV: case OPR_IDIV: + case OPR_MOD: case OPR_POW: + case OPR_BAND: case OPR_BOR: case OPR_BXOR: + case OPR_SHL: case OPR_SHR: { + if (!tonumeral(v, NULL)) luaK_exp2RK(fs, v); break; } default: { @@ -837,21 +909,23 @@ void luaK_posfix (FuncState *fs, BinOpr op, } else { luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ - codearith(fs, OP_CONCAT, e1, e2, line); + codeexpval(fs, OP_CONCAT, e1, e2, line); } break; } case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: - case OPR_MOD: case OPR_POW: { - codearith(fs, cast(OpCode, op - OPR_ADD + OP_ADD), e1, e2, line); + case OPR_IDIV: case OPR_MOD: case OPR_POW: + case OPR_BAND: case OPR_BOR: case OPR_BXOR: + case OPR_SHL: case OPR_SHR: { + codeexpval(fs, cast(OpCode, (op - OPR_ADD) + OP_ADD), e1, e2, line); break; } case OPR_EQ: case OPR_LT: case OPR_LE: { - codecomp(fs, cast(OpCode, op - OPR_EQ + OP_EQ), 1, e1, e2); + codecomp(fs, cast(OpCode, (op - OPR_EQ) + OP_EQ), 1, e1, e2); break; } case OPR_NE: case OPR_GT: case OPR_GE: { - codecomp(fs, cast(OpCode, op - OPR_NE + OP_EQ), 0, e1, e2); + codecomp(fs, cast(OpCode, (op - OPR_NE) + OP_EQ), 0, e1, e2); break; } default: lua_assert(0); diff --git a/deps/lua/lcode.h b/deps/lua/lcode.h index 5a8e0b8..b4c865f 100644 --- a/deps/lua/lcode.h +++ b/deps/lua/lcode.h @@ -1,5 +1,5 @@ /* -** $Id: lcode.h,v 1.58.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lcode.h,v 1.63 2013/12/30 20:47:58 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ @@ -24,7 +24,11 @@ ** grep "ORDER OPR" if you change these enums (ORDER OP) */ typedef enum BinOpr { - OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, + OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, + OPR_DIV, + OPR_IDIV, + OPR_BAND, OPR_BOR, OPR_BXOR, + OPR_SHL, OPR_SHR, OPR_CONCAT, OPR_EQ, OPR_LT, OPR_LE, OPR_NE, OPR_GT, OPR_GE, @@ -33,7 +37,7 @@ typedef enum BinOpr { } BinOpr; -typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; +typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; #define getcode(fs,e) ((fs)->f->sp->code[(e)->u.info]) @@ -52,7 +56,7 @@ LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); -LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); +LUAI_FUNC int luaK_intK (FuncState *fs, lua_Integer n); LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); diff --git a/deps/lua/lcorolib.c b/deps/lua/lcorolib.c index ce4f6ad..0c0b7fa 100644 --- a/deps/lua/lcorolib.c +++ b/deps/lua/lcorolib.c @@ -1,15 +1,16 @@ /* -** $Id: lcorolib.c,v 1.5.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lcorolib.c,v 1.9 2014/11/02 19:19:04 roberto Exp $ ** Coroutine Library ** See Copyright Notice in lua.h */ +#define lcorolib_c +#define LUA_LIB -#include +#include "lprefix.h" -#define lcorolib_c -#define LUA_LIB +#include #include "lua.h" @@ -17,6 +18,13 @@ #include "lualib.h" +static lua_State *getco (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "thread expected"); + return co; +} + + static int auxresume (lua_State *L, lua_State *co, int narg) { int status; if (!lua_checkstack(co, narg)) { @@ -47,9 +55,8 @@ static int auxresume (lua_State *L, lua_State *co, int narg) { static int luaB_coresume (lua_State *L) { - lua_State *co = lua_tothread(L, 1); + lua_State *co = getco(L); int r; - luaL_argcheck(L, co, 1, "coroutine expected"); r = auxresume(L, co, lua_gettop(L) - 1); if (r < 0) { lua_pushboolean(L, 0); @@ -59,7 +66,7 @@ static int luaB_coresume (lua_State *L) { else { lua_pushboolean(L, 1); lua_insert(L, -(r + 1)); - return r + 1; /* return true + `resume' returns */ + return r + 1; /* return true + 'resume' returns */ } } @@ -102,8 +109,7 @@ static int luaB_yield (lua_State *L) { static int luaB_costatus (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - luaL_argcheck(L, co, 1, "coroutine expected"); + lua_State *co = getco(L); if (L == co) lua_pushliteral(L, "running"); else { switch (lua_status(co)) { @@ -129,6 +135,12 @@ static int luaB_costatus (lua_State *L) { } +static int luaB_yieldable (lua_State *L) { + lua_pushboolean(L, lua_isyieldable(L)); + return 1; +} + + static int luaB_corunning (lua_State *L) { int ismain = lua_pushthread(L); lua_pushboolean(L, ismain); @@ -143,6 +155,7 @@ static const luaL_Reg co_funcs[] = { {"status", luaB_costatus}, {"wrap", luaB_cowrap}, {"yield", luaB_yield}, + {"isyieldable", luaB_yieldable}, {NULL, NULL} }; diff --git a/deps/lua/lctype.c b/deps/lua/lctype.c index 93f8cad..ae9367e 100644 --- a/deps/lua/lctype.c +++ b/deps/lua/lctype.c @@ -1,5 +1,5 @@ /* -** $Id: lctype.c,v 1.11.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lctype.c,v 1.12 2014/11/02 19:19:04 roberto Exp $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ @@ -7,6 +7,9 @@ #define lctype_c #define LUA_CORE +#include "lprefix.h" + + #include "lctype.h" #if !LUA_USE_CTYPE /* { */ diff --git a/deps/lua/lctype.h b/deps/lua/lctype.h index b09b21a..99c7d12 100644 --- a/deps/lua/lctype.h +++ b/deps/lua/lctype.h @@ -1,5 +1,5 @@ /* -** $Id: lctype.h,v 1.12.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lctype.h,v 1.12 2011/07/15 12:50:29 roberto Exp $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ diff --git a/deps/lua/ldblib.c b/deps/lua/ldblib.c index 84fe3c7..786f6cd 100644 --- a/deps/lua/ldblib.c +++ b/deps/lua/ldblib.c @@ -1,26 +1,42 @@ /* -** $Id: ldblib.c,v 1.132.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ldblib.c,v 1.151 2015/11/23 11:29:43 roberto Exp $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ +#define ldblib_c +#define LUA_LIB + +#include "lprefix.h" + #include #include #include -#define ldblib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" #include "lualib.h" -#define HOOKKEY "_HKEY" +/* +** The hook table at registry[&HOOKKEY] maps threads to their current +** hook function. (We only need the unique address of 'HOOKKEY'.) +*/ +static const int HOOKKEY = 0; +/* +** If L1 != L, L1 can be in any state, and therefore there are no +** guarantees about its stack space; any push in L1 must be +** checked. +*/ +static void checkstack (lua_State *L, lua_State *L1, int n) { + if (L != L1 && !lua_checkstack(L1, n)) + luaL_error(L, "stack overflow"); +} + static int db_getregistry (lua_State *L) { lua_pushvalue(L, LUA_REGISTRYINDEX); @@ -57,35 +73,20 @@ static int db_getuservalue (lua_State *L) { static int db_setuservalue (lua_State *L) { - if (lua_type(L, 1) == LUA_TLIGHTUSERDATA) - luaL_argerror(L, 1, "full userdata expected, got light userdata"); luaL_checktype(L, 1, LUA_TUSERDATA); - if (!lua_isnoneornil(L, 2)) - luaL_checktype(L, 2, LUA_TTABLE); + luaL_checkany(L, 2); lua_settop(L, 2); lua_setuservalue(L, 1); return 1; } -static void settabss (lua_State *L, const char *i, const char *v) { - lua_pushstring(L, v); - lua_setfield(L, -2, i); -} - - -static void settabsi (lua_State *L, const char *i, int v) { - lua_pushinteger(L, v); - lua_setfield(L, -2, i); -} - - -static void settabsb (lua_State *L, const char *i, int v) { - lua_pushboolean(L, v); - lua_setfield(L, -2, i); -} - - +/* +** Auxiliary function used by several library functions: check for +** an optional thread as function's first argument and set 'arg' with +** 1 if this argument is present (so that functions can skip it to +** access their other arguments) +*/ static lua_State *getthread (lua_State *L, int *arg) { if (lua_isthread(L, 1)) { *arg = 1; @@ -93,44 +94,74 @@ static lua_State *getthread (lua_State *L, int *arg) { } else { *arg = 0; - return L; + return L; /* function will operate over current thread */ } } +/* +** Variations of 'lua_settable', used by 'db_getinfo' to put results +** from 'lua_getinfo' into result table. Key is always a string; +** value can be a string, an int, or a boolean. +*/ +static void settabss (lua_State *L, const char *k, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, k); +} + +static void settabsi (lua_State *L, const char *k, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, k); +} + +static void settabsb (lua_State *L, const char *k, int v) { + lua_pushboolean(L, v); + lua_setfield(L, -2, k); +} + + +/* +** In function 'db_getinfo', the call to 'lua_getinfo' may push +** results on the stack; later it creates the result table to put +** these objects. Function 'treatstackoption' puts the result from +** 'lua_getinfo' on top of the result table so that it can call +** 'lua_setfield'. +*/ static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { - if (L == L1) { - lua_pushvalue(L, -2); - lua_remove(L, -3); - } + if (L == L1) + lua_rotate(L, -2, 1); /* exchange object and table */ else - lua_xmove(L1, L, 1); - lua_setfield(L, -2, fname); + lua_xmove(L1, L, 1); /* move object to the "main" stack */ + lua_setfield(L, -2, fname); /* put object into table */ } +/* +** Calls 'lua_getinfo' and collects all results in a new table. +** L1 needs stack space for an optional input (function) plus +** two optional outputs (function and line table) from function +** 'lua_getinfo'. +*/ static int db_getinfo (lua_State *L) { lua_Debug ar; int arg; lua_State *L1 = getthread(L, &arg); const char *options = luaL_optstring(L, arg+2, "flnStu"); - if (lua_isnumber(L, arg+1)) { - if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { + checkstack(L, L1, 3); + if (lua_isfunction(L, arg + 1)) { /* info about a function? */ + options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ + lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ + lua_xmove(L, L1, 1); + } + else { /* stack level */ + if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) { lua_pushnil(L); /* level out of range */ return 1; } } - else if (lua_isfunction(L, arg+1)) { - lua_pushfstring(L, ">%s", options); - options = lua_tostring(L, -1); - lua_pushvalue(L, arg+1); - lua_xmove(L, L1, 1); - } - else - return luaL_argerror(L, arg+1, "function or level expected"); if (!lua_getinfo(L1, options, &ar)) return luaL_argerror(L, arg+2, "invalid option"); - lua_createtable(L, 0, 2); + lua_newtable(L); /* table to collect results */ if (strchr(options, 'S')) { settabss(L, "source", ar.source); settabss(L, "short_src", ar.short_src); @@ -164,20 +195,22 @@ static int db_getlocal (lua_State *L) { lua_State *L1 = getthread(L, &arg); lua_Debug ar; const char *name; - int nvar = luaL_checkint(L, arg+2); /* local-variable index */ + int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */ if (lua_isfunction(L, arg + 1)) { /* function argument? */ lua_pushvalue(L, arg + 1); /* push function */ lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ - return 1; + return 1; /* return only name (there is no value) */ } else { /* stack-level argument */ - if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + int level = (int)luaL_checkinteger(L, arg + 1); + if (!lua_getstack(L1, level, &ar)) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); + checkstack(L, L1, 1); name = lua_getlocal(L1, &ar, nvar); if (name) { - lua_xmove(L1, L, 1); /* push local value */ + lua_xmove(L1, L, 1); /* move local value */ lua_pushstring(L, name); /* push name */ - lua_pushvalue(L, -2); /* re-order */ + lua_rotate(L, -2, 1); /* re-order */ return 2; } else { @@ -190,26 +223,36 @@ static int db_getlocal (lua_State *L) { static int db_setlocal (lua_State *L) { int arg; + const char *name; lua_State *L1 = getthread(L, &arg); lua_Debug ar; - if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + int level = (int)luaL_checkinteger(L, arg + 1); + int nvar = (int)luaL_checkinteger(L, arg + 2); + if (!lua_getstack(L1, level, &ar)) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); luaL_checkany(L, arg+3); lua_settop(L, arg+3); + checkstack(L, L1, 1); lua_xmove(L, L1, 1); - lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); + name = lua_setlocal(L1, &ar, nvar); + if (name == NULL) + lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ + lua_pushstring(L, name); return 1; } +/* +** get (if 'get' is true) or set an upvalue from a closure +*/ static int auxupvalue (lua_State *L, int get) { const char *name; - int n = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TFUNCTION); + int n = (int)luaL_checkinteger(L, 2); /* upvalue index */ + luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); if (name == NULL) return 0; lua_pushstring(L, name); - lua_insert(L, -(get+1)); + lua_insert(L, -(get+1)); /* no-op if get is false */ return get + 1; } @@ -225,13 +268,15 @@ static int db_setupvalue (lua_State *L) { } +/* +** Check whether a given upvalue from a given closure exists and +** returns its index +*/ static int checkupval (lua_State *L, int argf, int argnup) { - lua_Debug ar; - int nup = luaL_checkint(L, argnup); - luaL_checktype(L, argf, LUA_TFUNCTION); - lua_pushvalue(L, argf); - lua_getinfo(L, ">u", &ar); - luaL_argcheck(L, 1 <= nup && nup <= ar.nups, argnup, "invalid upvalue index"); + int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ + luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ + luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup, + "invalid upvalue index"); return nup; } @@ -253,26 +298,29 @@ static int db_upvaluejoin (lua_State *L) { } -#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY) - - +/* +** Call hook function registered at hook table for the current +** thread (if there is one) +*/ static void hookf (lua_State *L, lua_Debug *ar) { static const char *const hooknames[] = {"call", "return", "line", "count", "tail call"}; - gethooktable(L); + lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY); lua_pushthread(L); - lua_rawget(L, -2); - if (lua_isfunction(L, -1)) { - lua_pushstring(L, hooknames[(int)ar->event]); + if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ + lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ if (ar->currentline >= 0) - lua_pushinteger(L, ar->currentline); + lua_pushinteger(L, ar->currentline); /* push current line */ else lua_pushnil(L); lua_assert(lua_getinfo(L, "lS", ar)); - lua_call(L, 2, 0); + lua_call(L, 2, 0); /* call hook function */ } } +/* +** Convert a string mask (for 'sethook') into a bit mask +*/ static int makemask (const char *smask, int count) { int mask = 0; if (strchr(smask, 'c')) mask |= LUA_MASKCALL; @@ -283,6 +331,9 @@ static int makemask (const char *smask, int count) { } +/* +** Convert a bit mask (for 'gethook') into a string mask +*/ static char *unmakemask (int mask, char *smask) { int i = 0; if (mask & LUA_MASKCALL) smask[i++] = 'c'; @@ -297,26 +348,30 @@ static int db_sethook (lua_State *L) { int arg, mask, count; lua_Hook func; lua_State *L1 = getthread(L, &arg); - if (lua_isnoneornil(L, arg+1)) { + if (lua_isnoneornil(L, arg+1)) { /* no hook? */ lua_settop(L, arg+1); func = NULL; mask = 0; count = 0; /* turn off hooks */ } else { const char *smask = luaL_checkstring(L, arg+2); luaL_checktype(L, arg+1, LUA_TFUNCTION); - count = luaL_optint(L, arg+3, 0); + count = (int)luaL_optinteger(L, arg + 3, 0); func = hookf; mask = makemask(smask, count); } - if (gethooktable(L) == 0) { /* creating hook table? */ + if (lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY) == LUA_TNIL) { + lua_createtable(L, 0, 2); /* create a hook table */ + lua_pushvalue(L, -1); + lua_rawsetp(L, LUA_REGISTRYINDEX, &HOOKKEY); /* set it in position */ lua_pushstring(L, "k"); lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */ lua_pushvalue(L, -1); lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */ } - lua_pushthread(L1); lua_xmove(L1, L, 1); - lua_pushvalue(L, arg+1); - lua_rawset(L, -3); /* set new hook */ - lua_sethook(L1, func, mask, count); /* set hooks */ + checkstack(L, L1, 1); + lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */ + lua_pushvalue(L, arg + 1); /* value (hook function) */ + lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ + lua_sethook(L1, func, mask, count); return 0; } @@ -327,16 +382,19 @@ static int db_gethook (lua_State *L) { char buff[5]; int mask = lua_gethookmask(L1); lua_Hook hook = lua_gethook(L1); - if (hook != NULL && hook != hookf) /* external hook? */ + if (hook == NULL) /* no hook? */ + lua_pushnil(L); + else if (hook != hookf) /* external hook? */ lua_pushliteral(L, "external hook"); - else { - gethooktable(L); + else { /* hook table must exist */ + lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY); + checkstack(L, L1, 1); lua_pushthread(L1); lua_xmove(L1, L, 1); - lua_rawget(L, -2); /* get hook */ + lua_rawget(L, -2); /* 1st result = hooktable[L1] */ lua_remove(L, -2); /* remove hook table */ } - lua_pushstring(L, unmakemask(mask, buff)); - lua_pushinteger(L, lua_gethookcount(L1)); + lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ + lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ return 3; } @@ -344,13 +402,13 @@ static int db_gethook (lua_State *L) { static int db_debug (lua_State *L) { for (;;) { char buffer[250]; - luai_writestringerror("%s", "lua_debug> "); + lua_writestringerror("%s", "lua_debug> "); if (fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0) return 0; if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || lua_pcall(L, 0, 0, 0)) - luai_writestringerror("%s\n", lua_tostring(L, -1)); + lua_writestringerror("%s\n", lua_tostring(L, -1)); lua_settop(L, 0); /* remove eventual returns */ } } @@ -363,7 +421,7 @@ static int db_traceback (lua_State *L) { if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */ lua_pushvalue(L, arg + 1); /* return it untouched */ else { - int level = luaL_optint(L, arg + 2, (L == L1) ? 1 : 0); + int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0); luaL_traceback(L, L1, msg, level); } return 1; diff --git a/deps/lua/ldebug.c b/deps/lua/ldebug.c index 9097f67..8f692a8 100644 --- a/deps/lua/ldebug.c +++ b/deps/lua/ldebug.c @@ -1,18 +1,19 @@ /* -** $Id: ldebug.c,v 2.90.1.3 2013/05/16 16:04:15 roberto Exp $ +** $Id: ldebug.c,v 2.117 2015/11/02 18:48:07 roberto Exp $ ** Debug Interface ** See Copyright Notice in lua.h */ +#define ldebug_c +#define LUA_CORE + +#include "lprefix.h" + #include #include #include - -#define ldebug_c -#define LUA_CORE - #include "lua.h" #include "lapi.h" @@ -33,6 +34,10 @@ #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL) +/* Active Lua function (given call info) */ +#define ci_func(ci) (clLvalue((ci)->func)) + + static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); @@ -47,10 +52,26 @@ static int currentline (CallInfo *ci) { } +/* +** If function yielded, its 'func' can be in the 'extra' field. The +** next function restores 'func' to its correct value for debugging +** purposes. (It exchanges 'func' and 'extra'; so, when called again, +** after debugging, it also "re-restores" ** 'func' to its altered value. +*/ +static void swapextra (lua_State *L) { + if (L->status == LUA_YIELD) { + CallInfo *ci = L->ci; /* get function that yielded */ + StkId temp = ci->func; /* exchange its 'func' and 'extra' values */ + ci->func = restorestack(L, ci->extra); + ci->extra = savestack(L, temp); + } +} + + /* ** this function can be called asynchronous (e.g. during a signal) */ -LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { +LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { if (func == NULL || mask == 0) { /* turn off hooks? */ mask = 0; func = NULL; @@ -61,7 +82,6 @@ LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { L->basehookcount = count; resethookcount(L); L->hookmask = cast_byte(mask); - return 1; } @@ -106,7 +126,7 @@ static const char *upvalname (Proto *p, int uv) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) { int nparams = clLvalue(ci->func)->p->sp->numparams; - if (n >= ci->u.l.base - ci->func - nparams) + if (n >= cast_int(ci->u.l.base - ci->func) - nparams) return NULL; /* no such vararg */ else { *pos = ci->func + nparams + n; @@ -144,6 +164,7 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n, LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; lua_lock(L); + swapextra(L); if (ar == NULL) { /* information about non-active function? */ if (!isLfunction(L->top - 1)) /* not a Lua function? */ name = NULL; @@ -151,25 +172,30 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { name = luaF_getlocalname(clLvalue(L->top - 1)->p, n, 0); } else { /* active function; get information through 'ar' */ - StkId pos = 0; /* to avoid warnings */ + StkId pos = NULL; /* to avoid warnings */ name = findlocal(L, ar->i_ci, n, &pos); if (name) { setobj2s(L, L->top, pos); api_incr_top(L); } } + swapextra(L); lua_unlock(L); return name; } LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { - StkId pos = 0; /* to avoid warnings */ - const char *name = findlocal(L, ar->i_ci, n, &pos); + StkId pos = NULL; /* to avoid warnings */ + const char *name; lua_lock(L); - if (name) + swapextra(L); + name = findlocal(L, ar->i_ci, n, &pos); + if (name) { setobjs2s(L, pos, L->top - 1); - L->top--; /* pop value */ + L->top--; /* pop value */ + } + swapextra(L); lua_unlock(L); return name; } @@ -269,6 +295,7 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { CallInfo *ci; StkId func; lua_lock(L); + swapextra(L); if (*what == '>') { ci = NULL; func = L->top - 1; @@ -287,6 +314,7 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { setobjs2s(L, L->top, func); api_incr_top(L); } + swapextra(L); /* correct before option 'L', which can raise a mem. error */ if (strchr(what, 'L')) collectvalidlines(L, cl); lua_unlock(L); @@ -366,18 +394,13 @@ static int findsetreg (Proto *p, int lastpc, int reg) { case OP_JMP: { int b = GETARG_sBx(i); int dest = pc + 1 + b; - /* jump is forward and do not skip `lastpc'? */ + /* jump is forward and do not skip 'lastpc'? */ if (pc < dest && dest <= lastpc) { if (dest > jmptarget) jmptarget = dest; /* update 'jmptarget' */ } break; } - case OP_TEST: { - if (reg == a) /* jumped code can change 'a' */ - setreg = filterpc(pc, jmptarget); - break; - } default: if (testAMode(op) && reg == a) /* any instruction that set A */ setreg = filterpc(pc, jmptarget); @@ -397,7 +420,7 @@ static const char *getobjname (Proto *p, int lastpc, int reg, /* else try symbolic execution */ pc = findsetreg(p, lastpc, reg); if (pc != -1) { /* could find instruction? */ - Instruction i = p->sp->code[pc]; + Instruction i = p->sp->code[pc]; OpCode op = GET_OPCODE(i); switch (op) { case OP_MOVE: { @@ -443,10 +466,14 @@ static const char *getobjname (Proto *p, int lastpc, int reg, static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { - TMS tm; + TMS tm = (TMS)0; /* to avoid warnings */ Proto *p = ci_func(ci)->p; /* calling function */ int pc = currentpc(ci); /* calling instruction index */ Instruction i = p->sp->code[pc]; /* calling instruction */ + if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ + *name = "?"; + return "hook"; + } switch (GET_OPCODE(i)) { case OP_CALL: case OP_TAILCALL: /* get function name */ @@ -456,25 +483,27 @@ static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { return "for iterator"; } /* all other instructions can call only through metamethods */ - case OP_SELF: - case OP_GETTABUP: - case OP_GETTABLE: tm = TM_INDEX; break; - case OP_SETTABUP: - case OP_SETTABLE: tm = TM_NEWINDEX; break; - case OP_EQ: tm = TM_EQ; break; - case OP_ADD: tm = TM_ADD; break; - case OP_SUB: tm = TM_SUB; break; - case OP_MUL: tm = TM_MUL; break; - case OP_DIV: tm = TM_DIV; break; - case OP_MOD: tm = TM_MOD; break; - case OP_POW: tm = TM_POW; break; + case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: + tm = TM_INDEX; + break; + case OP_SETTABUP: case OP_SETTABLE: + tm = TM_NEWINDEX; + break; + case OP_ADD: case OP_SUB: case OP_MUL: case OP_MOD: + case OP_POW: case OP_DIV: case OP_IDIV: case OP_BAND: + case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: { + int offset = cast_int(GET_OPCODE(i)) - cast_int(OP_ADD); /* ORDER OP */ + tm = cast(TMS, offset + cast_int(TM_ADD)); /* ORDER TM */ + break; + } case OP_UNM: tm = TM_UNM; break; + case OP_BNOT: tm = TM_BNOT; break; case OP_LEN: tm = TM_LEN; break; + case OP_CONCAT: tm = TM_CONCAT; break; + case OP_EQ: tm = TM_EQ; break; case OP_LT: tm = TM_LT; break; case OP_LE: tm = TM_LE; break; - case OP_CONCAT: tm = TM_CONCAT; break; - default: - return NULL; /* else no useful name can be found */ + default: lua_assert(0); /* other instructions cannot call a function */ } *name = getstr(G(L)->tmname[tm]); return "metamethod"; @@ -485,17 +514,21 @@ static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { /* -** only ANSI way to check whether a pointer points to an array -** (used only for error messages, so efficiency is not a big concern) +** The subtraction of two potentially unrelated pointers is +** not ISO C, but it should not crash a program; the subsequent +** checks are ISO C and ensure a correct result. */ static int isinstack (CallInfo *ci, const TValue *o) { - StkId p; - for (p = ci->u.l.base; p < ci->top; p++) - if (o == p) return 1; - return 0; + ptrdiff_t i = o - ci->u.l.base; + return (0 <= i && i < (ci->top - ci->u.l.base) && ci->u.l.base + i == o); } +/* +** Checks whether value 'o' came from an upvalue. (That can only happen +** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on +** upvalues.) +*/ static const char *getupvalname (CallInfo *ci, const TValue *o, const char **name) { LClosure *c = ci_func(ci); @@ -510,10 +543,9 @@ static const char *getupvalname (CallInfo *ci, const TValue *o, } -l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { +static const char *varinfo (lua_State *L, const TValue *o) { + const char *name = NULL; /* to avoid warnings */ CallInfo *ci = L->ci; - const char *name = NULL; - const char *t = objtypename(o); const char *kind = NULL; if (isLua(ci)) { kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ @@ -521,26 +553,39 @@ l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { kind = getobjname(ci_func(ci)->p, currentpc(ci), cast_int(o - ci->u.l.base), &name); } - if (kind) - luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", - op, kind, name, t); - else - luaG_runerror(L, "attempt to %s a %s value", op, t); + return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : ""; } -l_noret luaG_concaterror (lua_State *L, StkId p1, StkId p2) { - if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; - lua_assert(!ttisstring(p1) && !ttisnumber(p1)); +l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + const char *t = objtypename(o); + luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o)); +} + + +l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { + if (ttisstring(p1) || cvt2str(p1)) p1 = p2; luaG_typeerror(L, p1, "concatenate"); } -l_noret luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { - TValue temp; - if (luaV_tonumber(p1, &temp) == NULL) - p2 = p1; /* first operand is wrong */ - luaG_typeerror(L, p2, "perform arithmetic on"); +l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, const char *msg) { + lua_Number temp; + if (!tonumber(p1, &temp)) /* first operand is wrong? */ + p2 = p1; /* now second is wrong */ + luaG_typeerror(L, p2, msg); +} + + +/* +** Error when both values are convertible to numbers, but not to integers +*/ +l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { + lua_Integer temp; + if (!tointeger(p1, &temp)) + p2 = p1; + luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); } @@ -554,40 +599,75 @@ l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { } -static void addinfo (lua_State *L, const char *msg) { - CallInfo *ci = L->ci; - if (isLua(ci)) { /* is Lua code? */ - char buff[LUA_IDSIZE]; /* add file:line information */ - int line = currentline(ci); - TString *src = ci_func(ci)->p->sp->source; - if (src) - luaO_chunkid(buff, getstr(src), LUA_IDSIZE); - else { /* no source available; use "?" instead */ - buff[0] = '?'; buff[1] = '\0'; - } - luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); +/* add src:line information to 'msg' */ +const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, + int line) { + char buff[LUA_IDSIZE]; + if (src) + luaO_chunkid(buff, getstr(src), LUA_IDSIZE); + else { /* no source available; use "?" instead */ + buff[0] = '?'; buff[1] = '\0'; } + return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } l_noret luaG_errormsg (lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); - if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top - 1, errfunc); /* push function */ - L->top++; - luaD_call(L, L->top - 2, 1, 0); /* call it */ + L->top++; /* assume EXTRA_STACK */ + luaD_callnoyield(L, L->top - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { + CallInfo *ci = L->ci; + const char *msg; va_list argp; va_start(argp, fmt); - addinfo(L, luaO_pushvfstring(L, fmt, argp)); + msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); + if (isLua(ci)) /* if Lua function, add source:line information */ + luaG_addinfo(L, msg, ci_func(ci)->p->sp->source, currentline(ci)); luaG_errormsg(L); } + +void luaG_traceexec (lua_State *L) { + CallInfo *ci = L->ci; + lu_byte mask = L->hookmask; + int counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); + if (counthook) + resethookcount(L); /* reset count */ + else if (!(mask & LUA_MASKLINE)) + return; /* no line hook and count != 0; nothing to be done */ + if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ + ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ + return; /* do not call hook again (VM yielded, so it did not move) */ + } + if (counthook) + luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */ + if (mask & LUA_MASKLINE) { + Proto *p = ci_func(ci)->p; + int npc = pcRel(ci->u.l.savedpc, p); + int newline = getfuncline(p, npc); + if (npc == 0 || /* call linehook when enter a new function, */ + ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ + newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ + luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */ + } + L->oldpc = ci->u.l.savedpc; + if (L->status == LUA_YIELD) { /* did hook yield? */ + if (counthook) + L->hookcount = 1; /* undo decrement to zero */ + ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ + ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ + ci->func = L->top - 1; /* protect stack below results */ + luaD_throw(L, LUA_YIELD); + } +} + diff --git a/deps/lua/ldebug.h b/deps/lua/ldebug.h index 04d646d..0579311 100644 --- a/deps/lua/ldebug.h +++ b/deps/lua/ldebug.h @@ -1,5 +1,5 @@ /* -** $Id: ldebug.h,v 2.7.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ldebug.h,v 2.14 2015/05/22 17:45:56 roberto Exp $ ** Auxiliary functions from Debug Interface module ** See Copyright Notice in lua.h */ @@ -13,22 +13,27 @@ #define pcRel(pc, p) (cast(int, (pc) - (p)->sp->code) - 1) -#define getfuncline(f,pc) (((f)->sp->lineinfo) ? (f)->sp->lineinfo[pc] : 0) +#define getfuncline(f,pc) (((f)->sp->lineinfo) ? (f)->sp->lineinfo[pc] : -1) #define resethookcount(L) (L->hookcount = L->basehookcount) -/* Active Lua function (given call info) */ -#define ci_func(ci) (clLvalue((ci)->func)) - LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *opname); -LUAI_FUNC l_noret luaG_concaterror (lua_State *L, StkId p1, StkId p2); -LUAI_FUNC l_noret luaG_aritherror (lua_State *L, const TValue *p1, +LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, + const char *msg); +LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, + TString *src, int line); LUAI_FUNC l_noret luaG_errormsg (lua_State *L); +LUAI_FUNC void luaG_traceexec (lua_State *L); + #endif diff --git a/deps/lua/ldo.c b/deps/lua/ldo.c index a66d7e4..b44e691 100644 --- a/deps/lua/ldo.c +++ b/deps/lua/ldo.c @@ -1,17 +1,19 @@ /* -** $Id: ldo.c,v 2.108.1.3 2013/11/08 18:22:50 roberto Exp $ +** $Id: ldo.c,v 2.150 2015/11/19 19:16:22 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ +#define ldo_c +#define LUA_CORE + +#include "lprefix.h" + #include #include #include -#define ldo_c -#define LUA_CORE - #include "lua.h" #include "lapi.h" @@ -33,6 +35,8 @@ +#define errorstatus(s) ((s) > LUA_YIELD) + /* ** {====================================================== @@ -46,30 +50,33 @@ ** C++ code, with _longjmp/_setjmp when asked to use them, and with ** longjmp/setjmp otherwise. */ -#if !defined(LUAI_THROW) +#if !defined(LUAI_THROW) /* { */ + +#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */ -#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* C++ exceptions */ #define LUAI_THROW(L,c) throw(c) #define LUAI_TRY(L,c,a) \ try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } #define luai_jmpbuf int /* dummy variable */ -#elif defined(LUA_USE_ULONGJMP) -/* in Unix, try _longjmp/_setjmp (more efficient) */ +#elif defined(LUA_USE_POSIX) /* }{ */ + +/* in POSIX, try _longjmp/_setjmp (more efficient) */ #define LUAI_THROW(L,c) _longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf -#else -/* default handling with long jumps */ +#else /* }{ */ + +/* ISO C handling with long jumps */ #define LUAI_THROW(L,c) longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf -#endif +#endif /* } */ -#endif +#endif /* } */ @@ -106,15 +113,19 @@ l_noret luaD_throw (lua_State *L, int errcode) { LUAI_THROW(L, L->errorJmp); /* jump to it */ } else { /* thread has no error handler */ + global_State *g = G(L); L->status = cast_byte(errcode); /* mark it as dead */ - if (G(L)->mainthread->errorJmp) { /* main thread has a handler? */ - setobjs2s(L, G(L)->mainthread->top++, L->top - 1); /* copy error obj. */ - luaD_throw(G(L)->mainthread, errcode); /* re-throw in main thread */ + if (g->mainthread->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ + luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ - if (G(L)->panic) { /* panic function? */ + if (g->panic) { /* panic function? */ + seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */ + if (L->ci->top < L->top) + L->ci->top = L->top; /* pushing msg. can break this invariant */ lua_unlock(L); - G(L)->panic(L); /* call it (last chance to jump out) */ + g->panic(L); /* call panic function (last chance to jump out) */ } abort(); } @@ -139,12 +150,17 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { /* }====================================================== */ +/* +** {================================================================== +** Stack reallocation +** =================================================================== +*/ static void correctstack (lua_State *L, TValue *oldstack) { CallInfo *ci; - GCObject *up; + UpVal *up; L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v = (up->v - oldstack) + L->stack; for (ci = L->ci; ci != NULL; ci = ci->previous) { ci->top = (ci->top - oldstack) + L->stack; ci->func = (ci->func - oldstack) + L->stack; @@ -206,14 +222,26 @@ void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK; if (goodsize > LUAI_MAXSTACK) goodsize = LUAI_MAXSTACK; - if (inuse > LUAI_MAXSTACK || /* handling stack overflow? */ - goodsize >= L->stacksize) /* would grow instead of shrink? */ - condmovestack(L); /* don't change stack (change only for debugging) */ + if (L->stacksize > LUAI_MAXSTACK) /* was handling stack overflow? */ + luaE_freeCI(L); /* free all CIs (list grew because of an error) */ else + luaE_shrinkCI(L); /* shrink list */ + if (inuse <= LUAI_MAXSTACK && /* not handling stack overflow? */ + goodsize < L->stacksize) /* trying to shrink? */ luaD_reallocstack(L, goodsize); /* shrink it */ + else + condmovestack(L,,); /* don't change stack (change only for debugging) */ } +void luaD_inctop (lua_State *L) { + luaD_checkstack(L, 1); + L->top++; +} + +/* }================================================================== */ + + void luaD_hook (lua_State *L, int event, int line) { lua_Hook hook = L->hook; if (hook && L->allowhook) { @@ -258,31 +286,34 @@ static StkId adjust_varargs (lua_State *L, SharedProto *p, int actual) { int i; int nfixargs = p->numparams; StkId base, fixed; - lua_assert(actual >= nfixargs); /* move fixed parameters to final position */ - luaD_checkstack(L, p->maxstacksize); /* check again for new 'base' */ fixed = L->top - actual; /* first fixed argument */ base = L->top; /* final position of first argument */ - for (i=0; itop++, fixed + i); - setnilvalue(fixed + i); + setnilvalue(fixed + i); /* erase original copy (for GC) */ } + for (; i < nfixargs; i++) + setnilvalue(L->top++); /* complete missing arguments */ return base; } -static StkId tryfuncTM (lua_State *L, StkId func) { +/* +** Check whether __call metafield of 'func' is a function. If so, put +** it in stack below original 'func' so that 'luaD_precall' can call +** it. Raise an error if __call metafield is not a function. +*/ +static void tryfuncTM (lua_State *L, StkId func) { const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); StkId p; - ptrdiff_t funcr = savestack(L, func); if (!ttisfunction(tm)) luaG_typeerror(L, func, "call"); - /* Open a hole inside the stack at `func' */ - for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); - incr_top(L); - func = restorestack(L, funcr); /* previous call may change stack */ + /* Open a hole inside the stack at 'func' */ + for (p = L->top; p > func; p--) + setobjs2s(L, p, p-1); + L->top++; /* slot ensured by caller */ setobj2s(L, func, tm); /* tag method is the new function to be called */ - return func; } @@ -290,79 +321,133 @@ static StkId tryfuncTM (lua_State *L, StkId func) { #define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L))) +/* macro to check stack size, preserving 'p' */ +#define checkstackp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ + luaC_checkGC(L), /* stack grow uses memory */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + /* -** returns true if function has been executed (C function) +** Prepares a function call: checks the stack, creates a new CallInfo +** entry, fills in the relevant information, calls hook if needed. +** If function is a C function, does the call, too. (Otherwise, leave +** the execution ('luaV_execute') to the caller, to allow stackless +** calls.) Returns true iff function has been executed (C function). */ int luaD_precall (lua_State *L, StkId func, int nresults) { lua_CFunction f; CallInfo *ci; - int n; /* number of arguments (Lua) or returns (C) */ - ptrdiff_t funcr = savestack(L, func); switch (ttype(func)) { + case LUA_TCCL: /* C closure */ + f = clCvalue(func)->f; + goto Cfunc; case LUA_TLCF: /* light C function */ f = fvalue(func); - goto Cfunc; - case LUA_TCCL: { /* C closure */ - f = clCvalue(func)->f; - Cfunc: - luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + Cfunc: { + int n; /* number of returns */ + checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; - ci->func = restorestack(L, funcr); + ci->func = func; ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); ci->callstatus = 0; - luaC_checkGC(L); /* stack grow uses memory */ if (L->hookmask & LUA_MASKCALL) luaD_hook(L, LUA_HOOKCALL, -1); lua_unlock(L); n = (*f)(L); /* do the actual call */ lua_lock(L); api_checknelems(L, n); - luaD_poscall(L, L->top - n); + luaD_poscall(L, ci, L->top - n, n); return 1; } case LUA_TLCL: { /* Lua function: prepare its call */ StkId base; SharedProto *p = clLvalue(func)->p->sp; - n = cast_int(L->top - func) - 1; /* number of real arguments */ - luaD_checkstack(L, p->maxstacksize); - for (; n < p->numparams; n++) - setnilvalue(L->top++); /* complete missing arguments */ - if (!p->is_vararg) { - func = restorestack(L, funcr); + int n = cast_int(L->top - func) - 1; /* number of real arguments */ + int fsize = p->maxstacksize; /* frame size */ + checkstackp(L, fsize, func); + if (p->is_vararg != 1) { /* do not use vararg? */ + for (; n < p->numparams; n++) + setnilvalue(L->top++); /* complete missing arguments */ base = func + 1; } - else { + else base = adjust_varargs(L, p, n); - func = restorestack(L, funcr); /* previous call can change stack */ - } ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; ci->func = func; ci->u.l.base = base; - ci->top = base + p->maxstacksize; + L->top = ci->top = base + fsize; lua_assert(ci->top <= L->stack_last); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus = CIST_LUA; - L->top = ci->top; - luaC_checkGC(L); /* stack grow uses memory */ if (L->hookmask & LUA_MASKCALL) callhook(L, ci); return 0; } default: { /* not a function */ - func = tryfuncTM(L, func); /* retry with 'function' tag method */ + checkstackp(L, 1, func); /* ensure space for metamethod */ + tryfuncTM(L, func); /* try to get '__call' metamethod */ return luaD_precall(L, func, nresults); /* now it must be a function */ } } } -int luaD_poscall (lua_State *L, StkId firstResult) { +/* +** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. +** Handle most typical cases (zero results for commands, one result for +** expressions, multiple results for tail calls/single parameters) +** separated. +*/ +static int moveresults (lua_State *L, const TValue *firstResult, StkId res, + int nres, int wanted) { + switch (wanted) { /* handle typical cases separately */ + case 0: break; /* nothing to move */ + case 1: { /* one result needed */ + if (nres == 0) /* no results? */ + firstResult = luaO_nilobject; /* adjust with nil */ + setobjs2s(L, res, firstResult); /* move it to proper place */ + break; + } + case LUA_MULTRET: { + int i; + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstResult + i); + L->top = res + nres; + return 0; /* wanted == LUA_MULTRET */ + } + default: { + int i; + if (wanted <= nres) { /* enough results? */ + for (i = 0; i < wanted; i++) /* move wanted results to correct place */ + setobjs2s(L, res + i, firstResult + i); + } + else { /* not enough results; use all of them plus nils */ + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstResult + i); + for (; i < wanted; i++) /* complete wanted number of results */ + setnilvalue(res + i); + } + break; + } + } + L->top = res + wanted; /* top points after the last result */ + return 1; +} + + +/* +** Finishes a function call: calls hook if necessary, removes CallInfo, +** moves current number of results to proper place; returns 0 iff call +** wanted multiple (variable number of) results. +*/ +int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres) { StkId res; - int wanted, i; - CallInfo *ci = L->ci; + int wanted = ci->nresults; if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) { if (L->hookmask & LUA_MASKRET) { ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */ @@ -372,15 +457,24 @@ int luaD_poscall (lua_State *L, StkId firstResult) { L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */ } res = ci->func; /* res == final position of 1st result */ - wanted = ci->nresults; - L->ci = ci = ci->previous; /* back to caller */ - /* move results to correct place */ - for (i = wanted; i != 0 && firstResult < L->top; i--) - setobjs2s(L, res++, firstResult++); - while (i-- > 0) - setnilvalue(res++); - L->top = res; - return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ + L->ci = ci->previous; /* back to caller */ + /* move results to proper place */ + return moveresults(L, firstResult, res, nres, wanted); +} + + +/* +** Check appropriate error for stack overflow ("regular" overflow or +** overflow while handling stack overflow). If 'nCalls' is larger than +** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but +** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to +** allow overflow handling to work) +*/ +static void stackerror (lua_State *L) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ } @@ -390,53 +484,67 @@ int luaD_poscall (lua_State *L, StkId firstResult) { ** When returns, all the results are on the stack, starting at the original ** function position. */ -void luaD_call (lua_State *L, StkId func, int nResults, int allowyield) { - if (++L->nCcalls >= LUAI_MAXCCALLS) { - if (L->nCcalls == LUAI_MAXCCALLS) - luaG_runerror(L, "C stack overflow"); - else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) - luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ - } - if (!allowyield) L->nny++; +void luaD_call (lua_State *L, StkId func, int nResults) { + if (++L->nCcalls >= LUAI_MAXCCALLS) + stackerror(L); if (!luaD_precall(L, func, nResults)) /* is a Lua function? */ luaV_execute(L); /* call it */ - if (!allowyield) L->nny--; L->nCcalls--; } -static void finishCcall (lua_State *L) { +/* +** Similar to 'luaD_call', but does not allow yields during the call +*/ +void luaD_callnoyield (lua_State *L, StkId func, int nResults) { + L->nny++; + luaD_call(L, func, nResults); + L->nny--; +} + + +/* +** Completes the execution of an interrupted C function, calling its +** continuation function. +*/ +static void finishCcall (lua_State *L, int status) { CallInfo *ci = L->ci; int n; - lua_assert(ci->u.c.k != NULL); /* must have a continuation */ - lua_assert(L->nny == 0); + /* must have a continuation and must be able to call it */ + lua_assert(ci->u.c.k != NULL && L->nny == 0); + /* error status can only happen in a protected call */ + lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD); if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */ ci->callstatus &= ~CIST_YPCALL; /* finish 'lua_pcall' */ L->errfunc = ci->u.c.old_errfunc; } - /* finish 'lua_callk'/'lua_pcall' */ + /* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already + handled */ adjustresults(L, ci->nresults); /* call continuation function */ - if (!(ci->callstatus & CIST_STAT)) /* no call status? */ - ci->u.c.status = LUA_YIELD; /* 'default' status */ - lua_assert(ci->u.c.status != LUA_OK); - ci->callstatus = (ci->callstatus & ~(CIST_YPCALL | CIST_STAT)) | CIST_YIELDED; lua_unlock(L); - n = (*ci->u.c.k)(L); + n = (*ci->u.c.k)(L, status, ci->u.c.ctx); lua_lock(L); api_checknelems(L, n); /* finish 'luaD_precall' */ - luaD_poscall(L, L->top - n); + luaD_poscall(L, ci, L->top - n, n); } +/* +** Executes "full continuation" (everything in the stack) of a +** previously interrupted coroutine until the stack is empty (or another +** interruption long-jumps out of the loop). If the coroutine is +** recovering from an error, 'ud' points to the error status, which must +** be passed to the first continuation function (otherwise the default +** status is LUA_YIELD). +*/ static void unroll (lua_State *L, void *ud) { - UNUSED(ud); - for (;;) { - if (L->ci == &L->base_ci) /* stack is empty? */ - return; /* coroutine finished normally */ + if (ud != NULL) /* error status? */ + finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */ + while (L->ci != &L->base_ci) { /* something in the stack */ if (!isLua(L->ci)) /* C function? */ - finishCcall(L); + finishCcall(L, LUA_YIELD); /* complete its execution */ else { /* Lua function */ luaV_finishOp(L); /* finish interrupted instruction */ luaV_execute(L); /* execute down to higher C 'boundary' */ @@ -446,7 +554,8 @@ static void unroll (lua_State *L, void *ud) { /* -** check whether thread has a suspended protected call +** Try to find a suspended protected call (a "recover point") for the +** given thread. */ static CallInfo *findpcall (lua_State *L) { CallInfo *ci; @@ -458,6 +567,11 @@ static CallInfo *findpcall (lua_State *L) { } +/* +** Recovers from an error in a coroutine. Finds a recover point (if +** there is one) and completes the execution of the interrupted +** 'luaD_pcall'. If there is no recover point, returns zero. +*/ static int recover (lua_State *L, int status) { StkId oldtop; CallInfo *ci = findpcall(L); @@ -467,12 +581,10 @@ static int recover (lua_State *L, int status) { luaF_close(L, oldtop); seterrorobj(L, status, oldtop); L->ci = ci; - L->allowhook = ci->u.c.old_allowhook; + L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ L->nny = 0; /* should be zero to be yieldable */ luaD_shrinkstack(L); L->errfunc = ci->u.c.old_errfunc; - ci->callstatus |= CIST_STAT; /* call has error status */ - ci->u.c.status = status; /* (here it is) */ return 1; /* continue running the coroutine */ } @@ -491,11 +603,16 @@ static l_noret resume_error (lua_State *L, const char *msg, StkId firstArg) { /* -** do the work for 'lua_resume' in protected mode +** Do the work for 'lua_resume' in protected mode. Most of the work +** depends on the status of the coroutine: initial state, suspended +** inside a hook, or regularly suspended (optionally with a continuation +** function), plus erroneous cases: non-suspended coroutine or dead +** coroutine. */ static void resume (lua_State *L, void *ud) { int nCcalls = L->nCcalls; - StkId firstArg = cast(StkId, ud); + int n = *(cast(int*, ud)); /* number of arguments */ + StkId firstArg = L->top - n; /* first argument */ CallInfo *ci = L->ci; if (nCcalls >= LUAI_MAXCCALLS) resume_error(L, "C stack overflow", firstArg); @@ -509,24 +626,21 @@ static void resume (lua_State *L, void *ud) { else if (L->status != LUA_YIELD) resume_error(L, "cannot resume dead coroutine", firstArg); else { /* resuming from previous yield */ - L->status = LUA_OK; + L->status = LUA_OK; /* mark that it is running (again) */ ci->func = restorestack(L, ci->extra); if (isLua(ci)) /* yielded inside a hook? */ luaV_execute(L); /* just continue running Lua code */ else { /* 'common' yield */ - if (ci->u.c.k != NULL) { /* does it have a continuation? */ - int n; - ci->u.c.status = LUA_YIELD; /* 'default' status */ - ci->callstatus |= CIST_YIELDED; + if (ci->u.c.k != NULL) { /* does it have a continuation function? */ lua_unlock(L); - n = (*ci->u.c.k)(L); /* call continuation */ + n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */ lua_lock(L); api_checknelems(L, n); firstArg = L->top - n; /* yield results come from continuation */ } - luaD_poscall(L, firstArg); /* finish 'luaD_precall' */ + luaD_poscall(L, ci, firstArg, n); /* finish 'luaD_precall' */ } - unroll(L, NULL); + unroll(L, NULL); /* run continuation */ } lua_assert(nCcalls == L->nCcalls); } @@ -534,27 +648,26 @@ static void resume (lua_State *L, void *ud) { LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) { int status; - int oldnny = L->nny; /* save 'nny' */ + unsigned short oldnny = L->nny; /* save "number of non-yieldable" calls */ lua_lock(L); luai_userstateresume(L, nargs); L->nCcalls = (from) ? from->nCcalls + 1 : 1; L->nny = 0; /* allow yields */ api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); - status = luaD_rawrunprotected(L, resume, L->top - nargs); + status = luaD_rawrunprotected(L, resume, &nargs); if (status == -1) /* error calling 'lua_resume'? */ status = LUA_ERRRUN; - else { /* yield or regular error */ - while (status != LUA_OK && status != LUA_YIELD) { /* error? */ - if (recover(L, status)) /* recover point? */ - status = luaD_rawrunprotected(L, unroll, NULL); /* run continuation */ - else { /* unrecoverable error */ - L->status = cast_byte(status); /* mark thread as `dead' */ - seterrorobj(L, status, L->top); - L->ci->top = L->top; - break; - } + else { /* continue running after recoverable errors */ + while (errorstatus(status) && recover(L, status)) { + /* unroll continuation */ + status = luaD_rawrunprotected(L, unroll, &status); + } + if (errorstatus(status)) { /* unrecoverable error? */ + L->status = cast_byte(status); /* mark thread as 'dead' */ + seterrorobj(L, status, L->top); /* push error message */ + L->ci->top = L->top; } - lua_assert(status == L->status); + else lua_assert(status == L->status); /* normal end or yield */ } L->nny = oldnny; /* restore 'nny' */ L->nCcalls--; @@ -564,7 +677,13 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) { } -LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { +LUA_API int lua_isyieldable (lua_State *L) { + return (L->nny == 0); +} + + +LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k) { CallInfo *ci = L->ci; luai_userstateyield(L, nresults); lua_lock(L); @@ -591,12 +710,6 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { return 0; /* return to 'luaD_hook' */ } -/* Added by xdczju@sina.com */ -LUA_API int lua_yieldable(lua_State *L) -{ - return L->nny == 0; -} - int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) { int status; @@ -624,7 +737,7 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u, /* ** Execute a protected parser. */ -struct SParser { /* data to `f_parser' */ +struct SParser { /* data to 'f_parser' */ ZIO *z; Mbuffer buff; /* dynamic structure used by the scanner */ Dyndata dyd; /* dynamic structures used by the parser */ @@ -636,31 +749,26 @@ struct SParser { /* data to `f_parser' */ static void checkmode (lua_State *L, const char *mode, const char *x) { if (mode && strchr(mode, x[0]) == NULL) { luaO_pushfstring(L, - "attempt to load a %s chunk (mode is " LUA_QS ")", x, mode); + "attempt to load a %s chunk (mode is '%s')", x, mode); luaD_throw(L, LUA_ERRSYNTAX); } } static void f_parser (lua_State *L, void *ud) { - int i; - Closure *cl; + LClosure *cl; struct SParser *p = cast(struct SParser *, ud); int c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { checkmode(L, p->mode, "binary"); - cl = luaU_undump(L, p->z, &p->buff, p->name); + cl = luaU_undump(L, p->z, p->name); } else { checkmode(L, p->mode, "text"); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); } - lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues); - for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */ - UpVal *up = luaF_newupval(L); - cl->l.upvals[i] = up; - luaC_objbarrier(L, cl, up); - } + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luaF_initupvals(L, cl); } diff --git a/deps/lua/ldo.h b/deps/lua/ldo.h index d3d3082..80582dc 100644 --- a/deps/lua/ldo.h +++ b/deps/lua/ldo.h @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 2.20.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ldo.h,v 2.28 2015/11/23 11:29:43 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -13,31 +13,43 @@ #include "lzio.h" -#define luaD_checkstack(L,n) if (L->stack_last - L->top <= (n)) \ - luaD_growstack(L, n); else condmovestack(L); +/* +** Macro to check stack size and grow stack if needed. Parameters +** 'pre'/'pos' allow the macro to preserve a pointer into the +** stack across reallocations, doing the work only when needed. +** 'condmovestack' is used in heavy tests to force a stack reallocation +** at every check. +*/ +#define luaD_checkstackaux(L,n,pre,pos) \ + if (L->stack_last - L->top <= (n)) \ + { pre; luaD_growstack(L, n); pos; } else { condmovestack(L,pre,pos); } + +/* In general, 'pre'/'pos' are empty (nothing to save) */ +#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,,) -#define incr_top(L) {L->top++; luaD_checkstack(L,0);} #define savestack(L,p) ((char *)(p) - (char *)L->stack) #define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) -/* type of protected functions, to be ran by `runprotected' */ +/* type of protected functions, to be ran by 'runprotected' */ typedef void (*Pfunc) (lua_State *L, void *ud); LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode); LUAI_FUNC void luaD_hook (lua_State *L, int event, int line); LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); -LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults, - int allowyield); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); -LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); +LUAI_FUNC int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, + int nres); LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); LUAI_FUNC void luaD_growstack (lua_State *L, int n); LUAI_FUNC void luaD_shrinkstack (lua_State *L); +LUAI_FUNC void luaD_inctop (lua_State *L); LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); diff --git a/deps/lua/ldump.c b/deps/lua/ldump.c index dd232ec..0621acb 100644 --- a/deps/lua/ldump.c +++ b/deps/lua/ldump.c @@ -1,174 +1,216 @@ /* -** $Id: ldump.c,v 2.17.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ldump.c,v 2.37 2015/10/08 15:53:49 roberto Exp $ ** save precompiled Lua chunks ** See Copyright Notice in lua.h */ -#include - #define ldump_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "lobject.h" #include "lstate.h" #include "lundump.h" + typedef struct { - lua_State* L; - lua_Writer writer; - void* data; - int strip; - int status; + lua_State *L; + lua_Writer writer; + void *data; + int strip; + int status; } DumpState; -#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) -#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) - -static void DumpBlock(const void* b, size_t size, DumpState* D) -{ - if (D->status==0) - { - lua_unlock(D->L); - D->status=(*D->writer)(D->L,b,size,D->data); - lua_lock(D->L); - } + +/* +** All high-level dumps go through DumpVector; you can change it to +** change the endianness of the result +*/ +#define DumpVector(v,n,D) DumpBlock(v,(n)*sizeof((v)[0]),D) + +#define DumpLiteral(s,D) DumpBlock(s, sizeof(s) - sizeof(char), D) + + +static void DumpBlock (const void *b, size_t size, DumpState *D) { + if (D->status == 0 && size > 0) { + lua_unlock(D->L); + D->status = (*D->writer)(D->L, b, size, D->data); + lua_lock(D->L); + } } -static void DumpChar(int y, DumpState* D) -{ - char x=(char)y; - DumpVar(x,D); + +#define DumpVar(x,D) DumpVector(&x,1,D) + + +static void DumpByte (int y, DumpState *D) { + lu_byte x = (lu_byte)y; + DumpVar(x, D); } -static void DumpInt(int x, DumpState* D) -{ - DumpVar(x,D); + +static void DumpInt (int x, DumpState *D) { + DumpVar(x, D); } -static void DumpNumber(lua_Number x, DumpState* D) -{ - DumpVar(x,D); + +static void DumpNumber (lua_Number x, DumpState *D) { + DumpVar(x, D); } -static void DumpVector(const void* b, int n, size_t size, DumpState* D) -{ - DumpInt(n,D); - DumpMem(b,n,size,D); + +static void DumpInteger (lua_Integer x, DumpState *D) { + DumpVar(x, D); } -static void DumpString(const TString* s, DumpState* D) -{ - if (s==NULL) - { - size_t size=0; - DumpVar(size,D); - } - else - { - size_t size=s->tsv.len+1; /* include trailing '\0' */ - DumpVar(size,D); - DumpBlock(getstr(s),size*sizeof(char),D); - } + +static void DumpString (const TString *s, DumpState *D) { + if (s == NULL) + DumpByte(0, D); + else { + size_t size = tsslen(s) + 1; /* include trailing '\0' */ + const char *str = getstr(s); + if (size < 0xFF) + DumpByte(cast_int(size), D); + else { + DumpByte(0xFF, D); + DumpVar(size, D); + } + DumpVector(str, size - 1, D); /* no need to save '\0' */ + } } -#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) - -static void DumpFunction(const Proto* f, DumpState* D); - -static void DumpConstants(const Proto* f, DumpState* D) -{ - int i,n=f->sp->sizek; - DumpInt(n,D); - for (i=0; ik[i]; - DumpChar(ttypenv(o),D); - switch (ttypenv(o)) - { - case LUA_TNIL: - break; - case LUA_TBOOLEAN: - DumpChar(bvalue(o),D); - break; - case LUA_TNUMBER: - DumpNumber(nvalue(o),D); - break; - case LUA_TSTRING: - DumpString(rawtsvalue(o),D); - break; - default: lua_assert(0); + +static void DumpCode (const SharedProto *f, DumpState *D) { + DumpInt(f->sizecode, D); + DumpVector(f->code, f->sizecode, D); +} + + +static void DumpFunction(const Proto *f, TString *psource, DumpState *D); + +static void DumpConstants (const Proto *f, DumpState *D) { + int i; + int n = f->sp->sizek; + DumpInt(n, D); + for (i = 0; i < n; i++) { + const TValue *o = &f->k[i]; + DumpByte(ttype(o), D); + switch (ttype(o)) { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + DumpByte(bvalue(o), D); + break; + case LUA_TNUMFLT: + DumpNumber(fltvalue(o), D); + break; + case LUA_TNUMINT: + DumpInteger(ivalue(o), D); + break; + case LUA_TSHRSTR: + case LUA_TLNGSTR: + DumpString(tsvalue(o), D); + break; + default: + lua_assert(0); + } } - } - n=f->sp->sizep; - DumpInt(n,D); - for (i=0; ip[i],D); } -static void DumpUpvalues(const Proto* f, DumpState* D) -{ - int i,n=f->sp->sizeupvalues; - DumpInt(n,D); - for (i=0; isp->upvalues[i].instack,D); - DumpChar(f->sp->upvalues[i].idx,D); - } + +static void DumpProtos (const Proto *f, DumpState *D) { + int i; + int n = f->sp->sizep; + DumpInt(n, D); + for (i = 0; i < n; i++) + DumpFunction(f->p[i], f->sp->source, D); +} + + +static void DumpUpvalues (const Proto *f, DumpState *D) { + int i, n = f->sp->sizeupvalues; + DumpInt(n, D); + for (i = 0; i < n; i++) { + DumpByte(f->sp->upvalues[i].instack, D); + DumpByte(f->sp->upvalues[i].idx, D); + } } -static void DumpDebug(const SharedProto* f, DumpState* D) -{ - int i,n; - DumpString((D->strip) ? NULL : f->source,D); - n= (D->strip) ? 0 : f->sizelineinfo; - DumpVector(f->lineinfo,n,sizeof(int),D); - n= (D->strip) ? 0 : f->sizelocvars; - DumpInt(n,D); - for (i=0; ilocvars[i].varname,D); - DumpInt(f->locvars[i].startpc,D); - DumpInt(f->locvars[i].endpc,D); - } - n= (D->strip) ? 0 : f->sizeupvalues; - DumpInt(n,D); - for (i=0; iupvalues[i].name,D); + +static void DumpDebug (const SharedProto *f, DumpState *D) { + int i, n; + n = (D->strip) ? 0 : f->sizelineinfo; + DumpInt(n, D); + DumpVector(f->lineinfo, n, D); + n = (D->strip) ? 0 : f->sizelocvars; + DumpInt(n, D); + for (i = 0; i < n; i++) { + DumpString(f->locvars[i].varname, D); + DumpInt(f->locvars[i].startpc, D); + DumpInt(f->locvars[i].endpc, D); + } + n = (D->strip) ? 0 : f->sizeupvalues; + DumpInt(n, D); + for (i = 0; i < n; i++) + DumpString(f->upvalues[i].name, D); } -static void DumpFunction(const Proto* f, DumpState* D) -{ - const SharedProto *sp = f->sp; - DumpInt(sp->linedefined,D); - DumpInt(sp->lastlinedefined,D); - DumpChar(sp->numparams,D); - DumpChar(sp->is_vararg,D); - DumpChar(sp->maxstacksize,D); - DumpCode(sp,D); - DumpConstants(f,D); - DumpUpvalues(f,D); - DumpDebug(sp,D); + +static void DumpFunction (const Proto *f, TString *psource, DumpState *D) { + const SharedProto *sp = f->sp; + if (D->strip || sp->source == psource) + DumpString(NULL, D); /* no debug info or same source as its parent */ + else + DumpString(sp->source, D); + DumpInt(sp->linedefined, D); + DumpInt(sp->lastlinedefined, D); + DumpByte(sp->numparams, D); + DumpByte(sp->is_vararg, D); + DumpByte(sp->maxstacksize, D); + DumpCode(sp, D); + DumpConstants(f, D); + DumpUpvalues(f, D); + DumpProtos(f, D); + DumpDebug(sp, D); } -static void DumpHeader(DumpState* D) -{ - lu_byte h[LUAC_HEADERSIZE]; - luaU_header(h); - DumpBlock(h,LUAC_HEADERSIZE,D); + +static void DumpHeader (DumpState *D) { + DumpLiteral(LUA_SIGNATURE, D); + DumpByte(LUAC_VERSION, D); + DumpByte(LUAC_FORMAT, D); + DumpLiteral(LUAC_DATA, D); + DumpByte(sizeof(int), D); + DumpByte(sizeof(size_t), D); + DumpByte(sizeof(Instruction), D); + DumpByte(sizeof(lua_Integer), D); + DumpByte(sizeof(lua_Number), D); + DumpInteger(LUAC_INT, D); + DumpNumber(LUAC_NUM, D); } + /* ** dump Lua function as precompiled chunk */ -int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) -{ - DumpState D; - D.L=L; - D.writer=w; - D.data=data; - D.strip=strip; - D.status=0; - DumpHeader(&D); - DumpFunction(f,&D); - return D.status; +int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, + int strip) { + DumpState D; + D.L = L; + D.writer = w; + D.data = data; + D.strip = strip; + D.status = 0; + DumpHeader(&D); + DumpByte(f->sp->sizeupvalues, &D); + DumpFunction(f, NULL, &D); + return D.status; } + diff --git a/deps/lua/lfunc.c b/deps/lua/lfunc.c index d7f83d8..d4bde84 100644 --- a/deps/lua/lfunc.c +++ b/deps/lua/lfunc.c @@ -1,15 +1,17 @@ /* -** $Id: lfunc.c,v 2.30.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lfunc.c,v 2.45 2014/11/02 19:19:04 roberto Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ - -#include - #define lfunc_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "lfunc.h" @@ -20,95 +22,83 @@ -Closure *luaF_newCclosure (lua_State *L, int n) { - Closure *c = &luaC_newobj(L, LUA_TCCL, sizeCclosure(n), NULL, 0)->cl; - c->c.nupvalues = cast_byte(n); +CClosure *luaF_newCclosure (lua_State *L, int n) { + GCObject *o = luaC_newobj(L, LUA_TCCL, sizeCclosure(n)); + CClosure *c = gco2ccl(o); + c->nupvalues = cast_byte(n); return c; } -Closure *luaF_newLclosure (lua_State *L, int n) { - Closure *c = &luaC_newobj(L, LUA_TLCL, sizeLclosure(n), NULL, 0)->cl; - c->l.p = NULL; - c->l.nupvalues = cast_byte(n); - while (n--) c->l.upvals[n] = NULL; +LClosure *luaF_newLclosure (lua_State *L, int n) { + GCObject *o = luaC_newobj(L, LUA_TLCL, sizeLclosure(n)); + LClosure *c = gco2lcl(o); + c->p = NULL; + c->nupvalues = cast_byte(n); + while (n--) c->upvals[n] = NULL; return c; } - -UpVal *luaF_newupval (lua_State *L) { - UpVal *uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), NULL, 0)->uv; - uv->v = &uv->u.value; - setnilvalue(uv->v); - return uv; +/* +** fill a closure with new closed upvalues +*/ +void luaF_initupvals (lua_State *L, LClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) { + UpVal *uv = luaM_new(L, UpVal); + uv->refcount = 1; + uv->v = &uv->u.value; /* make it closed */ + setnilvalue(uv->v); + cl->upvals[i] = uv; + } } UpVal *luaF_findupval (lua_State *L, StkId level) { - global_State *g = G(L); - GCObject **pp = &L->openupval; + UpVal **pp = &L->openupval; UpVal *p; UpVal *uv; - while (*pp != NULL && (p = gco2uv(*pp))->v >= level) { - GCObject *o = obj2gco(p); - lua_assert(p->v != &p->u.value); - lua_assert(!isold(o) || isold(obj2gco(L))); - if (p->v == level) { /* found a corresponding upvalue? */ - if (isdead(g, o)) /* is it dead? */ - changewhite(o); /* resurrect it */ - return p; - } - pp = &p->next; + lua_assert(isintwups(L) || L->openupval == NULL); + while (*pp != NULL && (p = *pp)->v >= level) { + lua_assert(upisopen(p)); + if (p->v == level) /* found a corresponding upvalue? */ + return p; /* return it */ + pp = &p->u.open.next; } - /* not found: create a new one */ - uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), pp, 0)->uv; + /* not found: create a new upvalue */ + uv = luaM_new(L, UpVal); + uv->refcount = 0; + uv->u.open.next = *pp; /* link it to list of open upvalues */ + uv->u.open.touched = 1; + *pp = uv; uv->v = level; /* current value lives in the stack */ - uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ - uv->u.l.next = g->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - g->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ + L->twups = G(L)->twups; /* link it to the list */ + G(L)->twups = L; + } return uv; } -static void unlinkupval (UpVal *uv) { - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ - uv->u.l.prev->u.l.next = uv->u.l.next; -} - - -void luaF_freeupval (lua_State *L, UpVal *uv) { - if (uv->v != &uv->u.value) /* is it open? */ - unlinkupval(uv); /* remove from open list */ - luaM_free(L, uv); /* free upvalue */ -} - - void luaF_close (lua_State *L, StkId level) { UpVal *uv; - global_State *g = G(L); - while (L->openupval != NULL && (uv = gco2uv(L->openupval))->v >= level) { - GCObject *o = obj2gco(uv); - lua_assert(!isblack(o) && uv->v != &uv->u.value); - L->openupval = uv->next; /* remove from `open' list */ - if (isdead(g, o)) - luaF_freeupval(L, uv); /* free upvalue */ + while (L->openupval != NULL && (uv = L->openupval)->v >= level) { + lua_assert(upisopen(uv)); + L->openupval = uv->u.open.next; /* remove from 'open' list */ + if (uv->refcount == 0) /* no references? */ + luaM_free(L, uv); /* free upvalue */ else { - unlinkupval(uv); /* remove upvalue from 'uvhead' list */ setobj(L, &uv->u.value, uv->v); /* move value to upvalue slot */ uv->v = &uv->u.value; /* now current value lives here */ - gch(o)->next = g->allgc; /* link upvalue into 'allgc' list */ - g->allgc = o; - luaC_checkupvalcolor(g, uv); + luaC_upvalbarrier(L, uv); } } } Proto *luaF_newproto (lua_State *L, SharedProto *sp) { - Proto *f = &luaC_newobj(L, LUA_TPROTO, sizeof(Proto), NULL, 0)->p; + GCObject *o = luaC_newobj(L, LUA_TPROTO, sizeof(Proto)); + Proto *f = gco2p(o); f->k = NULL; f->sp = NULL; f->p = NULL; @@ -169,9 +159,8 @@ void luaF_freeproto (lua_State *L, Proto *f) { luaM_free(L, f); } - /* -** Look for n-th local variable at line `line' in function `func'. +** Look for n-th local variable at line 'line' in function 'func'. ** Returns NULL if not found. */ const char *luaF_getlocalname (const Proto *fp, int local_number, int pc) { diff --git a/deps/lua/lfunc.h b/deps/lua/lfunc.h index ba31596..21cbe34 100644 --- a/deps/lua/lfunc.h +++ b/deps/lua/lfunc.h @@ -1,5 +1,5 @@ /* -** $Id: lfunc.h,v 2.8.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lfunc.h,v 2.15 2015/01/13 15:49:11 roberto Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ @@ -18,14 +18,42 @@ cast(int, sizeof(TValue *)*((n)-1))) +/* test whether thread is in 'twups' list */ +#define isintwups(L) (L->twups != L) + + +/* +** maximum number of upvalues in a closure (both C and Lua). (Value +** must fit in a VM register.) +*/ +#define MAXUPVAL 255 + + +/* +** Upvalues for Lua closures +*/ +struct UpVal { + TValue *v; /* points to stack or to its own value */ + lu_mem refcount; /* reference counter */ + union { + struct { /* (when open) */ + UpVal *next; /* linked list */ + int touched; /* mark to avoid cycles with dead threads */ + } open; + TValue value; /* the value (when closed) */ + } u; +}; + +#define upisopen(up) ((up)->v != &(up)->u.value) + + LUAI_FUNC Proto *luaF_newproto (lua_State *L, SharedProto *sp); -LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems); -LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems); -LUAI_FUNC UpVal *luaF_newupval (lua_State *L); +LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); +LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); +LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_close (lua_State *L, StkId level); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); -LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, int pc); diff --git a/deps/lua/lgc.c b/deps/lua/lgc.c index c0f2858..c2d2a7d 100644 --- a/deps/lua/lgc.c +++ b/deps/lua/lgc.c @@ -1,14 +1,17 @@ /* -** $Id: lgc.c,v 2.140.1.2 2013/04/26 18:22:05 roberto Exp $ +** $Id: lgc.c,v 2.210 2015/11/03 18:10:44 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ -#include - #define lgc_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "ldebug.h" @@ -23,6 +26,11 @@ #include "ltm.h" +/* +** internal state for collector while inside the atomic phase. The +** collector should never be in this state while running regular code. +*/ +#define GCSinsideatomic (GCSpause + 1) /* ** cost of sweeping one element (the size of a small object divided @@ -33,8 +41,8 @@ /* maximum number of elements to sweep in each single step */ #define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4)) -/* maximum number of finalizers to call in each GC step */ -#define GCFINALIZENUM 4 +/* cost of calling one finalizer */ +#define GCFINALIZECOST GCSWEEPCOST /* @@ -52,18 +60,18 @@ /* -** 'makewhite' erases all color bits plus the old bit and then -** sets only the current white bit +** 'makewhite' erases all color bits then sets only the current white +** bit */ -#define maskcolors (~(bit2mask(BLACKBIT, OLDBIT) | WHITEBITS)) +#define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS)) #define makewhite(g,x) \ - (gch(x)->marked = cast_byte((gch(x)->marked & maskcolors) | luaC_white(g))) + (x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g))) -#define white2gray(x) resetbits(gch(x)->marked, WHITEBITS) -#define black2gray(x) resetbit(gch(x)->marked, BLACKBIT) +#define white2gray(x) resetbits(x->marked, WHITEBITS) +#define black2gray(x) resetbit(x->marked, BLACKBIT) -#define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT) +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) #define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) @@ -75,8 +83,13 @@ #define markvalue(g,o) { checkconsistency(o); \ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } -#define markobject(g,t) { if ((t) && iswhite(obj2gco(t))) \ - reallymarkobject(g, obj2gco(t)); } +#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } + +/* +** mark an object that can be NULL (either because it is really optional, +** or it was stripped as debug info, or inside an uncompleted structure) +*/ +#define markobjectN(g,t) { if (t) markobject(g,t); } static void reallymarkobject (global_State *g, GCObject *o); @@ -95,33 +108,38 @@ static void reallymarkobject (global_State *g, GCObject *o); /* -** link table 'h' into list pointed by 'p' +** link collectable object 'o' into list pointed by 'p' */ -#define linktable(h,p) ((h)->gclist = *(p), *(p) = obj2gco(h)) +#define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) /* -** if key is not marked, mark its entry as dead (therefore removing it -** from the table) +** If key is not marked, mark its entry as dead. This allows key to be +** collected, but keeps its entry in the table. A dead node is needed +** when Lua looks up for a key (it may be part of a chain) and when +** traversing a weak table (key might be removed from the table during +** traversal). Other places never manipulate dead keys, because its +** associated nil value is enough to signal that the entry is logically +** empty. */ static void removeentry (Node *n) { lua_assert(ttisnil(gval(n))); if (valiswhite(gkey(n))) - setdeadvalue(gkey(n)); /* unused and unmarked key; remove it */ + setdeadvalue(wgkey(n)); /* unused and unmarked key; remove it */ } /* ** tells whether a key or value can be cleared from a weak ** table. Non-collectable objects are never removed from weak -** tables. Strings behave as `values', so are never removed too. for +** tables. Strings behave as 'values', so are never removed too. for ** other objects: if really collected, cannot keep them; for objects ** being finalized, keep them in keys, but not in values */ static int iscleared (global_State *g, const TValue *o) { if (!iscollectable(o)) return 0; else if (ttisstring(o)) { - markobject(g, rawtsvalue(o)); /* strings are `values', so are never weak */ + markobject(g, tsvalue(o)); /* strings are 'values', so are never weak */ return 0; } else return iswhite(gcvalue(o)); @@ -130,14 +148,14 @@ static int iscleared (global_State *g, const TValue *o) { /* ** barrier that moves collector forward, that is, mark the white object -** being pointed by a black object. +** being pointed by a black object. (If in sweep phase, clear the black +** object to white [sweep it] to avoid other barrier calls for this +** same object.) */ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); - lua_assert(g->gcstate != GCSpause); - lua_assert(gch(o)->tt != LUA_TTABLE); - if (keepinvariantout(g)) /* must keep invariant? */ + if (keepinvariant(g)) /* must keep invariant? */ reallymarkobject(g, v); /* restore invariant */ else { /* sweep phase */ lua_assert(issweepphase(g)); @@ -148,78 +166,52 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { /* ** barrier that moves collector backward, that is, mark the black object -** pointing to a white object as gray again. (Current implementation -** only works for tables; access to 'gclist' is not uniform across -** different types.) +** pointing to a white object as gray again. */ -void luaC_barrierback_ (lua_State *L, GCObject *o) { +void luaC_barrierback_ (lua_State *L, Table *t) { global_State *g = G(L); - lua_assert(isblack(o) && !isdead(g, o) && gch(o)->tt == LUA_TTABLE); - black2gray(o); /* make object gray (again) */ - gco2t(o)->gclist = g->grayagain; - g->grayagain = o; + lua_assert(isblack(t) && !isdead(g, t)); + black2gray(t); /* make table gray (again) */ + linkgclist(t, g->grayagain); } /* -** barrier for prototypes. When creating first closure (cache is -** NULL), use a forward barrier; this may be the only closure of the -** prototype (if it is a "regular" function, with a single instance) -** and the prototype may be big, so it is better to avoid traversing -** it again. Otherwise, use a backward barrier, to avoid marking all -** possible instances. +** barrier for assignments to closed upvalues. Because upvalues are +** shared among closures, it is impossible to know the color of all +** closures pointing to it. So, we assume that the object being assigned +** must be marked. */ -LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c) { +void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) { global_State *g = G(L); - lua_assert(isblack(obj2gco(p))); - if (p->cache == NULL) { /* first time? */ - luaC_objbarrier(L, p, c); - } - else { /* use a backward barrier */ - black2gray(obj2gco(p)); /* make prototype gray (again) */ - p->gclist = g->grayagain; - g->grayagain = obj2gco(p); - } + GCObject *o = gcvalue(uv->v); + lua_assert(!upisopen(uv)); /* ensured by macro luaC_upvalbarrier */ + if (keepinvariant(g)) + markobject(g, o); } -/* -** check color (and invariants) for an upvalue that was closed, -** i.e., moved into the 'allgc' list -*/ -void luaC_checkupvalcolor (global_State *g, UpVal *uv) { - GCObject *o = obj2gco(uv); - lua_assert(!isblack(o)); /* open upvalues are never black */ - if (isgray(o)) { - if (keepinvariant(g)) { - resetoldbit(o); /* see MOVE OLD rule */ - gray2black(o); /* it is being visited now */ - markvalue(g, uv->v); - } - else { - lua_assert(issweepphase(g)); - makewhite(g, o); - } - } +void luaC_fix (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ + white2gray(o); /* they will be gray forever */ + g->allgc = o->next; /* remove object from 'allgc' list */ + o->next = g->fixedgc; /* link it to 'fixedgc' list */ + g->fixedgc = o; } /* ** create a new collectable object (with given type and size) and link -** it to '*list'. 'offset' tells how many bytes to allocate before the -** object itself (used only by states). +** it to 'allgc' list. */ -GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list, - int offset) { +GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { global_State *g = G(L); - char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz)); - GCObject *o = obj2gco(raw + offset); - if (list == NULL) - list = &g->allgc; /* standard list for collectable objects */ - gch(o)->marked = luaC_white(g); - gch(o)->tt = tt; - gch(o)->next = *list; - *list = o; + GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); + o->marked = luaC_white(g); + o->tt = tt; + o->next = g->allgc; + g->allgc = o; return o; } @@ -241,57 +233,53 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list, ** upvalues are already linked in 'headuv' list.) */ static void reallymarkobject (global_State *g, GCObject *o) { - lu_mem size; + reentry: white2gray(o); - switch (gch(o)->tt) { - case LUA_TSHRSTR: - case LUA_TLNGSTR: { - size = sizestring(gco2ts(o)); - break; /* nothing else to mark; make it black */ + switch (o->tt) { + case LUA_TSHRSTR: { + gray2black(o); + g->GCmemtrav += sizelstring(gco2ts(o)->shrlen); + break; } - case LUA_TUSERDATA: { - Table *mt = gco2u(o)->metatable; - markobject(g, mt); - markobject(g, gco2u(o)->env); - size = sizeudata(gco2u(o)); + case LUA_TLNGSTR: { + gray2black(o); + g->GCmemtrav += sizelstring(gco2ts(o)->u.lnglen); break; } - case LUA_TUPVAL: { - UpVal *uv = gco2uv(o); - markvalue(g, uv->v); - if (uv->v != &uv->u.value) /* open? */ - return; /* open upvalues remain gray */ - size = sizeof(UpVal); + case LUA_TUSERDATA: { + TValue uvalue; + markobjectN(g, gco2u(o)->metatable); /* mark its metatable */ + gray2black(o); + g->GCmemtrav += sizeudata(gco2u(o)); + getuservalue(g->mainthread, gco2u(o), &uvalue); + if (valiswhite(&uvalue)) { /* markvalue(g, &uvalue); */ + o = gcvalue(&uvalue); + goto reentry; + } break; } case LUA_TLCL: { - gco2lcl(o)->gclist = g->gray; - g->gray = o; - return; + linkgclist(gco2lcl(o), g->gray); + break; } case LUA_TCCL: { - gco2ccl(o)->gclist = g->gray; - g->gray = o; - return; + linkgclist(gco2ccl(o), g->gray); + break; } case LUA_TTABLE: { - linktable(gco2t(o), &g->gray); - return; + linkgclist(gco2t(o), g->gray); + break; } case LUA_TTHREAD: { - gco2th(o)->gclist = g->gray; - g->gray = o; - return; + linkgclist(gco2th(o), g->gray); + break; } case LUA_TPROTO: { - gco2p(o)->gclist = g->gray; - g->gray = o; - return; + linkgclist(gco2p(o), g->gray); + break; } - default: lua_assert(0); return; + default: lua_assert(0); break; } - gray2black(o); - g->GCmemtrav += size; } @@ -301,7 +289,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { static void markmt (global_State *g) { int i; for (i=0; i < LUA_NUMTAGS; i++) - markobject(g, g->mt[i]); + markobjectN(g, g->mt[i]); } @@ -310,29 +298,41 @@ static void markmt (global_State *g) { */ static void markbeingfnz (global_State *g) { GCObject *o; - for (o = g->tobefnz; o != NULL; o = gch(o)->next) { - makewhite(g, o); - reallymarkobject(g, o); - } + for (o = g->tobefnz; o != NULL; o = o->next) + markobject(g, o); } /* -** mark all values stored in marked open upvalues. (See comment in -** 'lstate.h'.) +** Mark all values stored in marked open upvalues from non-marked threads. +** (Values from marked threads were already marked when traversing the +** thread.) Remove from the list threads that no longer have upvalues and +** not-marked threads. */ static void remarkupvals (global_State *g) { - UpVal *uv; - for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { - if (isgray(obj2gco(uv))) - markvalue(g, uv->v); + lua_State *thread; + lua_State **p = &g->twups; + while ((thread = *p) != NULL) { + lua_assert(!isblack(thread)); /* threads are never black */ + if (isgray(thread) && thread->openupval != NULL) + p = &thread->twups; /* keep marked thread with upvalues in the list */ + else { /* thread is not marked or without upvalues */ + UpVal *uv; + *p = thread->twups; /* remove thread from the list */ + thread->twups = thread; /* mark that it is out of list */ + for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { + if (uv->u.open.touched) { + markvalue(g, uv->v); /* remark upvalue's value */ + uv->u.open.touched = 0; + } + } + } } } /* -** mark root set and reset all gray lists, to start a new -** incremental (or full) collection +** mark root set and reset all gray lists, to start a new collection */ static void restartcollection (global_State *g) { g->gray = g->grayagain = NULL; @@ -352,12 +352,18 @@ static void restartcollection (global_State *g) { ** ======================================================= */ +/* +** Traverse a table with weak values and link it to proper list. During +** propagate phase, keep it in 'grayagain' list, to be revisited in the +** atomic phase. In the atomic phase, if table has any white value, +** put it in 'weak' list, to be cleared. +*/ static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); - /* if there is array part, assume it may have white values (do not - traverse it just to check) */ + /* if there is array part, assume it may have white values (it is not + worth traversing it now just to check) */ int hasclears = (h->sizearray > 0); - for (n = gnode(h, 0); n < limit; n++) { + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ @@ -368,20 +374,30 @@ static void traverseweakvalue (global_State *g, Table *h) { hasclears = 1; /* table will have to be cleared */ } } - if (hasclears) - linktable(h, &g->weak); /* has to be cleared later */ - else /* no white values */ - linktable(h, &g->grayagain); /* no need to clean */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasclears) + linkgclist(h, g->weak); /* has to be cleared later */ } +/* +** Traverse an ephemeron table and link it to proper list. Returns true +** iff any object was marked during this traversal (which implies that +** convergence has to continue). During propagation phase, keep table +** in 'grayagain' list, to be visited again in the atomic phase. In +** the atomic phase, if table has any white->white entry, it has to +** be revisited during ephemeron convergence (as that key may turn +** black). Otherwise, if it has any white key, table has to be cleared +** (in the atomic phase). +*/ static int traverseephemeron (global_State *g, Table *h) { int marked = 0; /* true if an object is marked in this traversal */ int hasclears = 0; /* true if table has white keys */ - int prop = 0; /* true if table has entry "white-key -> white-value" */ + int hasww = 0; /* true if table has entry "white-key -> white-value" */ Node *n, *limit = gnodelast(h); - int i; - /* traverse array part (numeric keys are 'strong') */ + unsigned int i; + /* traverse array part */ for (i = 0; i < h->sizearray; i++) { if (valiswhite(&h->array[i])) { marked = 1; @@ -396,26 +412,27 @@ static int traverseephemeron (global_State *g, Table *h) { else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */ hasclears = 1; /* table must be cleared */ if (valiswhite(gval(n))) /* value not marked yet? */ - prop = 1; /* must propagate again */ + hasww = 1; /* white-white entry */ } else if (valiswhite(gval(n))) { /* value not marked yet? */ marked = 1; reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ } } - if (prop) - linktable(h, &g->ephemeron); /* have to propagate again */ - else if (hasclears) /* does table have white keys? */ - linktable(h, &g->allweak); /* may have to clean white keys */ - else /* no white keys */ - linktable(h, &g->grayagain); /* no need to clean */ + /* link table into proper list */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasww) /* table has white->white entries? */ + linkgclist(h, g->ephemeron); /* have to propagate again */ + else if (hasclears) /* table has white keys? */ + linkgclist(h, g->allweak); /* may have to clean white keys */ return marked; } static void traversestrongtable (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); - int i; + unsigned int i; for (i = 0; i < h->sizearray; i++) /* traverse array part */ markvalue(g, &h->array[i]); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ @@ -434,18 +451,18 @@ static void traversestrongtable (global_State *g, Table *h) { static lu_mem traversetable (global_State *g, Table *h) { const char *weakkey, *weakvalue; const TValue *mode = gfasttm(g, h->metatable, TM_MODE); - markobject(g, h->metatable); + markobjectN(g, h->metatable); if (mode && ttisstring(mode) && /* is there a weak mode? */ ((weakkey = strchr(svalue(mode), 'k')), (weakvalue = strchr(svalue(mode), 'v')), (weakkey || weakvalue))) { /* is really weak? */ - black2gray(obj2gco(h)); /* keep table gray */ + black2gray(h); /* keep table gray */ if (!weakkey) /* strong keys? */ traverseweakvalue(g, h); else if (!weakvalue) /* strong values? */ traverseephemeron(g, h); else /* all weak */ - linktable(h, &g->allweak); /* nothing to traverse now */ + linkgclist(h, g->allweak); /* nothing to traverse now */ } else /* not weak */ traversestrongtable(g, h); @@ -458,32 +475,35 @@ marksharedproto (global_State *g, SharedProto *f) { int i; if (g != f->l_G) return 0; - markobject(g, f->source); + markobjectN(g, f->source); for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ - markobject(g, f->upvalues[i].name); + markobjectN(g, f->upvalues[i].name); for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ - markobject(g, f->locvars[i].varname); + markobjectN(g, f->locvars[i].varname); return sizeof(Instruction) * f->sizecode + sizeof(int) * f->sizelineinfo + sizeof(LocVar) * f->sizelocvars + sizeof(Upvaldesc) * f->sizeupvalues; } - +/* +** Traverse a prototype. (While a prototype is being build, its +** arrays can be larger than needed; the extra slots are filled with +** NULL, so the use of 'markobjectN') +*/ static int traverseproto (global_State *g, Proto *f) { int i; - if (f->cache && iswhite(obj2gco(f->cache))) + if (f->cache && iswhite(f->cache)) f->cache = NULL; /* allow cache to be collected */ for (i = 0; i < f->sp->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i = 0; i < f->sp->sizep; i++) /* mark nested protos */ - markobject(g, f->p[i]); + markobjectN(g, f->p[i]); return sizeof(Proto) + sizeof(Proto *) * f->sp->sizep + sizeof(TValue) * f->sp->sizek + marksharedproto(g, f->sp); } - static lu_mem traverseCclosure (global_State *g, CClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ @@ -491,34 +511,50 @@ static lu_mem traverseCclosure (global_State *g, CClosure *cl) { return sizeCclosure(cl->nupvalues); } +/* +** open upvalues point to values in a thread, so those values should +** be marked when the thread is traversed except in the atomic phase +** (because then the value cannot be changed by the thread and the +** thread may not be traversed again) +*/ static lu_mem traverseLclosure (global_State *g, LClosure *cl) { int i; - markobject(g, cl->p); /* mark its prototype */ - for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ - markobject(g, cl->upvals[i]); + markobjectN(g, cl->p); /* mark its prototype */ + for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ + UpVal *uv = cl->upvals[i]; + if (uv != NULL) { + if (upisopen(uv) && g->gcstate != GCSinsideatomic) + uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ + else + markvalue(g, uv->v); + } + } return sizeLclosure(cl->nupvalues); } -static lu_mem traversestack (global_State *g, lua_State *th) { - int n = 0; +static lu_mem traversethread (global_State *g, lua_State *th) { StkId o = th->stack; if (o == NULL) return 1; /* stack not completely built yet */ + lua_assert(g->gcstate == GCSinsideatomic || + th->openupval == NULL || isintwups(th)); for (; o < th->top; o++) /* mark live elements in the stack */ markvalue(g, o); - if (g->gcstate == GCSatomic) { /* final traversal? */ + if (g->gcstate == GCSinsideatomic) { /* final traversal? */ StkId lim = th->stack + th->stacksize; /* real end of stack */ for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(o); + /* 'remarkupvals' may have removed thread from 'twups' list */ + if (!isintwups(th) && th->openupval != NULL) { + th->twups = g->twups; /* link it back to the list */ + g->twups = th; + } } - else { /* count call infos to compute size */ - CallInfo *ci; - for (ci = &th->base_ci; ci != th->ci; ci = ci->next) - n++; - } - return sizeof(lua_State) + sizeof(TValue) * th->stacksize + - sizeof(CallInfo) * n; + else if (g->gckind != KGC_EMERGENCY) + luaD_shrinkstack(th); /* do not change stack in emergency cycle */ + return (sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * th->nci); } @@ -531,7 +567,7 @@ static void propagatemark (global_State *g) { GCObject *o = g->gray; lua_assert(isgray(o)); gray2black(o); - switch (gch(o)->tt) { + switch (o->tt) { case LUA_TTABLE: { Table *h = gco2t(o); g->gray = h->gclist; /* remove from 'gray' list */ @@ -553,10 +589,9 @@ static void propagatemark (global_State *g) { case LUA_TTHREAD: { lua_State *th = gco2th(o); g->gray = th->gclist; /* remove from 'gray' list */ - th->gclist = g->grayagain; - g->grayagain = o; /* insert into 'grayagain' list */ + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ black2gray(o); - size = traversestack(g, th); + size = traversethread(g, th); break; } case LUA_TPROTO: { @@ -576,35 +611,12 @@ static void propagateall (global_State *g) { } -static void propagatelist (global_State *g, GCObject *l) { - lua_assert(g->gray == NULL); /* no grays left */ - g->gray = l; - propagateall(g); /* traverse all elements from 'l' */ -} - -/* -** retraverse all gray lists. Because tables may be reinserted in other -** lists when traversed, traverse the original lists to avoid traversing -** twice the same table (which is not wrong, but inefficient) -*/ -static void retraversegrays (global_State *g) { - GCObject *weak = g->weak; /* save original lists */ - GCObject *grayagain = g->grayagain; - GCObject *ephemeron = g->ephemeron; - g->weak = g->grayagain = g->ephemeron = NULL; - propagateall(g); /* traverse main gray list */ - propagatelist(g, grayagain); - propagatelist(g, weak); - propagatelist(g, ephemeron); -} - - static void convergeephemerons (global_State *g) { int changed; do { GCObject *w; GCObject *next = g->ephemeron; /* get ephemeron list */ - g->ephemeron = NULL; /* tables will return to this list when traversed */ + g->ephemeron = NULL; /* tables may return to this list when traversed */ changed = 0; while ((w = next) != NULL) { next = gco2t(w)->gclist; @@ -652,7 +664,7 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) { for (; l != f; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); - int i; + unsigned int i; for (i = 0; i < h->sizearray; i++) { TValue *o = &h->array[i]; if (iscleared(g, o)) /* value was collected? */ @@ -668,26 +680,45 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) { } +void luaC_upvdeccount (lua_State *L, UpVal *uv) { + lua_assert(uv->refcount > 0); + uv->refcount--; + if (uv->refcount == 0 && !upisopen(uv)) + luaM_free(L, uv); +} + + +static void freeLclosure (lua_State *L, LClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) { + UpVal *uv = cl->upvals[i]; + if (uv) + luaC_upvdeccount(L, uv); + } + luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); +} + + static void freeobj (lua_State *L, GCObject *o) { - switch (gch(o)->tt) { + switch (o->tt) { case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; case LUA_TLCL: { - luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); + freeLclosure(L, gco2lcl(o)); break; } case LUA_TCCL: { luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); break; } - case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; case LUA_TTABLE: luaH_free(L, gco2t(o)); break; case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break; case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break; case LUA_TSHRSTR: - G(L)->strt.nuse--; - /* go through */ + luaS_remove(L, gco2ts(o)); /* remove it from hash table */ + luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); + break; case LUA_TLNGSTR: { - luaM_freemem(L, o, sizestring(gco2ts(o))); + luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); break; } default: lua_assert(0); @@ -699,61 +730,27 @@ static void freeobj (lua_State *L, GCObject *o) { static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count); -/* -** sweep the (open) upvalues of a thread and resize its stack and -** list of call-info structures. -*/ -static void sweepthread (lua_State *L, lua_State *L1) { - if (L1->stack == NULL) return; /* stack not completely built yet */ - sweepwholelist(L, &L1->openupval); /* sweep open upvalues */ - luaE_freeCI(L1); /* free extra CallInfo slots */ - /* should not change the stack during an emergency gc cycle */ - if (G(L)->gckind != KGC_EMERGENCY) - luaD_shrinkstack(L1); -} - - /* ** sweep at most 'count' elements from a list of GCObjects erasing dead -** objects, where a dead (not alive) object is one marked with the "old" -** (non current) white and not fixed. -** In non-generational mode, change all non-dead objects back to white, -** preparing for next collection cycle. -** In generational mode, keep black objects black, and also mark them as -** old; stop when hitting an old object, as all objects after that -** one will be old too. -** When object is a thread, sweep its list of open upvalues too. +** objects, where a dead object is one marked with the old (non current) +** white; change all non-dead objects back to white, preparing for next +** collection cycle. Return where to continue the traversal or NULL if +** list is finished. */ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { global_State *g = G(L); int ow = otherwhite(g); - int toclear, toset; /* bits to clear and to set in all live objects */ - int tostop; /* stop sweep when this is true */ - if (isgenerational(g)) { /* generational mode? */ - toclear = ~0; /* clear nothing */ - toset = bitmask(OLDBIT); /* set the old bit of all surviving objects */ - tostop = bitmask(OLDBIT); /* do not sweep old generation */ - } - else { /* normal mode */ - toclear = maskcolors; /* clear all color bits + old bit */ - toset = luaC_white(g); /* make object white */ - tostop = 0; /* do not stop */ - } + int white = luaC_white(g); /* current white */ while (*p != NULL && count-- > 0) { GCObject *curr = *p; - int marked = gch(curr)->marked; + int marked = curr->marked; if (isdeadm(ow, marked)) { /* is 'curr' dead? */ - *p = gch(curr)->next; /* remove 'curr' from list */ + *p = curr->next; /* remove 'curr' from list */ freeobj(L, curr); /* erase 'curr' */ } - else { - if (testbits(marked, tostop)) - return NULL; /* stop sweeping this list */ - if (gch(curr)->tt == LUA_TTHREAD) - sweepthread(L, gco2th(curr)); /* sweep thread's upvalues */ - /* update marks */ - gch(curr)->marked = cast_byte((marked & toclear) | toset); - p = &gch(curr)->next; /* go to next element */ + else { /* change mark to 'white' */ + curr->marked = cast_byte((marked & maskcolors) | white); + p = &curr->next; /* go to next element */ } } return (*p == NULL) ? NULL : p; @@ -764,7 +761,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { ** sweep a list until a live object (or end of list) */ static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) { - GCObject ** old = p; + GCObject **old = p; int i = 0; do { i++; @@ -783,26 +780,27 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) { ** ======================================================= */ -static void checkSizes (lua_State *L) { - global_State *g = G(L); - if (g->gckind != KGC_EMERGENCY) { /* do not change sizes in emergency */ - int hs = g->strt.size / 2; /* half the size of the string table */ - if (g->strt.nuse < cast(lu_int32, hs)) /* using less than that half? */ - luaS_resize(L, hs); /* halve its size */ - luaZ_freebuffer(L, &g->buff); /* free concatenation buffer */ +/* +** If possible, shrink string table +*/ +static void checkSizes (lua_State *L, global_State *g) { + if (g->gckind != KGC_EMERGENCY) { + l_mem olddebt = g->GCdebt; + if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ + luaS_resize(L, g->strt.size / 2); /* shrink it a little */ + g->GCestimate += g->GCdebt - olddebt; /* update estimate */ } } static GCObject *udata2finalize (global_State *g) { GCObject *o = g->tobefnz; /* get first element */ - lua_assert(isfinalized(o)); - g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */ - gch(o)->next = g->allgc; /* return it to 'allgc' list */ + lua_assert(tofinalize(o)); + g->tobefnz = o->next; /* remove it from 'tobefnz' list */ + o->next = g->allgc; /* return it to 'allgc' list */ g->allgc = o; - resetbit(gch(o)->marked, SEPARATED); /* mark that it is not in 'tobefnz' */ - lua_assert(!isold(o)); /* see MOVE OLD rule */ - if (!keepinvariantout(g)) /* not keeping invariant? */ + resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ + if (issweepphase(g)) makewhite(g, o); /* "sweep" object */ return o; } @@ -810,7 +808,7 @@ static GCObject *udata2finalize (global_State *g) { static void dothecall (lua_State *L, void *ud) { UNUSED(ud); - luaD_call(L, L->top - 2, 0, 0); + luaD_callnoyield(L, L->top - 2, 0); } @@ -846,29 +844,58 @@ static void GCTM (lua_State *L, int propagateerrors) { } +/* +** call a few (up to 'g->gcfinnum') finalizers +*/ +static int runafewfinalizers (lua_State *L) { + global_State *g = G(L); + unsigned int i; + lua_assert(!g->tobefnz || g->gcfinnum > 0); + for (i = 0; g->tobefnz && i < g->gcfinnum; i++) + GCTM(L, 1); /* call one finalizer */ + g->gcfinnum = (!g->tobefnz) ? 0 /* nothing more to finalize? */ + : g->gcfinnum * 2; /* else call a few more next time */ + return i; +} + + +/* +** call all pending finalizers +*/ +static void callallpendingfinalizers (lua_State *L, int propagateerrors) { + global_State *g = G(L); + while (g->tobefnz) + GCTM(L, propagateerrors); +} + + +/* +** find last 'next' field in list 'p' list (to add elements in its end) +*/ +static GCObject **findlast (GCObject **p) { + while (*p != NULL) + p = &(*p)->next; + return p; +} + + /* ** move all unreachable objects (or 'all' objects) that need ** finalization from list 'finobj' to list 'tobefnz' (to be finalized) */ -static void separatetobefnz (lua_State *L, int all) { - global_State *g = G(L); - GCObject **p = &g->finobj; +static void separatetobefnz (global_State *g, int all) { GCObject *curr; - GCObject **lastnext = &g->tobefnz; - /* find last 'next' field in 'tobefnz' list (to add elements in its end) */ - while (*lastnext != NULL) - lastnext = &gch(*lastnext)->next; + GCObject **p = &g->finobj; + GCObject **lastnext = findlast(&g->tobefnz); while ((curr = *p) != NULL) { /* traverse all finalizable objects */ - lua_assert(!isfinalized(curr)); - lua_assert(testbit(gch(curr)->marked, SEPARATED)); + lua_assert(tofinalize(curr)); if (!(iswhite(curr) || all)) /* not being collected? */ - p = &gch(curr)->next; /* don't bother with it */ + p = &curr->next; /* don't bother with it */ else { - l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ - *p = gch(curr)->next; /* remove 'curr' from 'finobj' list */ - gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */ + *p = curr->next; /* remove 'curr' from 'finobj' list */ + curr->next = *lastnext; /* link at the end of 'tobefnz' list */ *lastnext = curr; - lastnext = &gch(curr)->next; + lastnext = &curr->next; } } } @@ -880,33 +907,29 @@ static void separatetobefnz (lua_State *L, int all) { */ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); - if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */ - isfinalized(o) || /* ... or is finalized... */ - gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ + if (tofinalize(o) || /* obj. is already marked... */ + gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; - GCheader *ho = gch(o); - if (g->sweepgc == &ho->next) { /* avoid removing current sweep object */ - lua_assert(issweepphase(g)); - g->sweepgc = sweeptolive(L, g->sweepgc, NULL); + if (issweepphase(g)) { + makewhite(g, o); /* "sweep" object 'o' */ + if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ + g->sweepgc = sweeptolive(L, g->sweepgc, NULL); /* change 'sweepgc' */ } /* search for pointer pointing to 'o' */ - for (p = &g->allgc; *p != o; p = &gch(*p)->next) { /* empty */ } - *p = ho->next; /* remove 'o' from root list */ - ho->next = g->finobj; /* link it in list 'finobj' */ + for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } + *p = o->next; /* remove 'o' from 'allgc' list */ + o->next = g->finobj; /* link it in 'finobj' list */ g->finobj = o; - l_setbit(ho->marked, SEPARATED); /* mark it as such */ - if (!keepinvariantout(g)) /* not keeping invariant? */ - makewhite(g, o); /* "sweep" object */ - else - resetoldbit(o); /* see MOVE OLD rule */ + l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ } } /* }====================================================== */ + /* ** {====================================================== ** GC control @@ -915,195 +938,168 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { /* -** set a reasonable "time" to wait before starting a new GC cycle; -** cycle will start when memory use hits threshold +** Set a reasonable "time" to wait before starting a new GC cycle; cycle +** will start when memory use hits threshold. (Division by 'estimate' +** should be OK: it cannot be zero (because Lua cannot even start with +** less than PAUSEADJ bytes). */ -static void setpause (global_State *g, l_mem estimate) { - l_mem debt, threshold; - estimate = estimate / PAUSEADJ; /* adjust 'estimate' */ +static void setpause (global_State *g) { + l_mem threshold, debt; + l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ + lua_assert(estimate > 0); threshold = (g->gcpause < MAX_LMEM / estimate) /* overflow? */ ? estimate * g->gcpause /* no overflow */ : MAX_LMEM; /* overflow; truncate to maximum */ - debt = -cast(l_mem, threshold - gettotalbytes(g)); + debt = gettotalbytes(g) - threshold; luaE_setdebt(g, debt); } -#define sweepphases \ - (bitmask(GCSsweepstring) | bitmask(GCSsweepudata) | bitmask(GCSsweep)) - - /* -** enter first sweep phase (strings) and prepare pointers for other -** sweep phases. The calls to 'sweeptolive' make pointers point to an -** object inside the list (instead of to the header), so that the real -** sweep do not need to skip objects created between "now" and the start -** of the real sweep. +** Enter first sweep phase. +** The call to 'sweeptolive' makes pointer point to an object inside +** the list (instead of to the header), so that the real sweep do not +** need to skip objects created between "now" and the start of the real +** sweep. ** Returns how many objects it swept. */ static int entersweep (lua_State *L) { global_State *g = G(L); int n = 0; - g->gcstate = GCSsweepstring; - lua_assert(g->sweepgc == NULL && g->sweepfin == NULL); - /* prepare to sweep strings, finalizable objects, and regular objects */ - g->sweepstrgc = 0; - g->sweepfin = sweeptolive(L, &g->finobj, &n); + g->gcstate = GCSswpallgc; + lua_assert(g->sweepgc == NULL); g->sweepgc = sweeptolive(L, &g->allgc, &n); return n; } -/* -** change GC mode -*/ -void luaC_changemode (lua_State *L, int mode) { - global_State *g = G(L); - if (mode == g->gckind) return; /* nothing to change */ - if (mode == KGC_GEN) { /* change to generational mode */ - /* make sure gray lists are consistent */ - luaC_runtilstate(L, bitmask(GCSpropagate)); - g->GCestimate = gettotalbytes(g); - g->gckind = KGC_GEN; - } - else { /* change to incremental mode */ - /* sweep all objects to turn them back to white - (as white has not changed, nothing extra will be collected) */ - g->gckind = KGC_NORMAL; - entersweep(L); - luaC_runtilstate(L, ~sweepphases); - } -} - - -/* -** call all pending finalizers -*/ -static void callallpendingfinalizers (lua_State *L, int propagateerrors) { - global_State *g = G(L); - while (g->tobefnz) { - resetoldbit(g->tobefnz); - GCTM(L, propagateerrors); - } -} - - void luaC_freeallobjects (lua_State *L) { global_State *g = G(L); - int i; - separatetobefnz(L, 1); /* separate all objects with finalizers */ + separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); callallpendingfinalizers(L, 0); + lua_assert(g->tobefnz == NULL); g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */ g->gckind = KGC_NORMAL; - sweepwholelist(L, &g->finobj); /* finalizers can create objs. in 'finobj' */ + sweepwholelist(L, &g->finobj); sweepwholelist(L, &g->allgc); - for (i = 0; i < g->strt.size; i++) /* free all string lists */ - sweepwholelist(L, &g->strt.hash[i]); + sweepwholelist(L, &g->fixedgc); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); } static l_mem atomic (lua_State *L) { global_State *g = G(L); - l_mem work = -cast(l_mem, g->GCmemtrav); /* start counting work */ + l_mem work; GCObject *origweak, *origall; - lua_assert(!iswhite(obj2gco(g->mainthread))); + GCObject *grayagain = g->grayagain; /* save original list */ + lua_assert(g->ephemeron == NULL && g->weak == NULL); + lua_assert(!iswhite(g->mainthread)); + g->gcstate = GCSinsideatomic; + g->GCmemtrav = 0; /* start counting work */ markobject(g, L); /* mark running thread */ /* registry and global metatables may be changed by API */ markvalue(g, &g->l_registry); - markmt(g); /* mark basic metatables */ + markmt(g); /* mark global metatables */ /* remark occasional upvalues of (maybe) dead threads */ remarkupvals(g); propagateall(g); /* propagate changes */ - work += g->GCmemtrav; /* stop counting (do not (re)count grays) */ - /* traverse objects caught by write barrier and by 'remarkupvals' */ - retraversegrays(g); - work -= g->GCmemtrav; /* restart counting */ + work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ + g->gray = grayagain; + propagateall(g); /* traverse 'grayagain' list */ + g->GCmemtrav = 0; /* restart counting */ convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ - /* clear values from weak tables, before checking finalizers */ + /* Clear values from weak tables, before checking finalizers */ clearvalues(g, g->weak, NULL); clearvalues(g, g->allweak, NULL); origweak = g->weak; origall = g->allweak; work += g->GCmemtrav; /* stop counting (objects being finalized) */ - separatetobefnz(L, 0); /* separate objects to be finalized */ + separatetobefnz(g, 0); /* separate objects to be finalized */ + g->gcfinnum = 1; /* there may be objects to be finalized */ markbeingfnz(g); /* mark objects that will be finalized */ - propagateall(g); /* remark, to propagate `preserveness' */ - work -= g->GCmemtrav; /* restart counting */ + propagateall(g); /* remark, to propagate 'resurrection' */ + g->GCmemtrav = 0; /* restart counting */ convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ - clearkeys(g, g->allweak, NULL); /* clear keys from all allweak tables */ + clearkeys(g, g->allweak, NULL); /* clear keys from all 'allweak' tables */ /* clear values from resurrected weak tables */ clearvalues(g, g->weak, origweak); clearvalues(g, g->allweak, origall); + luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ work += g->GCmemtrav; /* complete counting */ return work; /* estimate of memory marked by 'atomic' */ } +static lu_mem sweepstep (lua_State *L, global_State *g, + int nextstate, GCObject **nextlist) { + if (g->sweepgc) { + l_mem olddebt = g->GCdebt; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); + g->GCestimate += g->GCdebt - olddebt; /* update estimate */ + if (g->sweepgc) /* is there still something to sweep? */ + return (GCSWEEPMAX * GCSWEEPCOST); + } + /* else enter next state */ + g->gcstate = nextstate; + g->sweepgc = nextlist; + return 0; +} + + static lu_mem singlestep (lua_State *L) { global_State *g = G(L); switch (g->gcstate) { case GCSpause: { - /* start to count memory traversed */ g->GCmemtrav = g->strt.size * sizeof(GCObject*); - lua_assert(!isgenerational(g)); restartcollection(g); g->gcstate = GCSpropagate; return g->GCmemtrav; } case GCSpropagate: { - if (g->gray) { - lu_mem oldtrav = g->GCmemtrav; - propagatemark(g); - return g->GCmemtrav - oldtrav; /* memory traversed in this step */ - } - else { /* no more `gray' objects */ - lu_mem work; - int sw; - g->gcstate = GCSatomic; /* finish mark phase */ - g->GCestimate = g->GCmemtrav; /* save what was counted */; - work = atomic(L); /* add what was traversed by 'atomic' */ - g->GCestimate += work; /* estimate of total memory traversed */ - sw = entersweep(L); - return work + sw * GCSWEEPCOST; - } + g->GCmemtrav = 0; + lua_assert(g->gray); + propagatemark(g); + if (g->gray == NULL) /* no more gray objects? */ + g->gcstate = GCSatomic; /* finish propagate phase */ + return g->GCmemtrav; /* memory traversed in this step */ } - case GCSsweepstring: { - int i; - for (i = 0; i < GCSWEEPMAX && g->sweepstrgc + i < g->strt.size; i++) - sweepwholelist(L, &g->strt.hash[g->sweepstrgc + i]); - g->sweepstrgc += i; - if (g->sweepstrgc >= g->strt.size) /* no more strings to sweep? */ - g->gcstate = GCSsweepudata; - return i * GCSWEEPCOST; + case GCSatomic: { + lu_mem work; + int sw; + propagateall(g); /* make sure gray list is empty */ + work = atomic(L); /* work is what was traversed by 'atomic' */ + sw = entersweep(L); + g->GCestimate = gettotalbytes(g); /* first estimate */; + return work + sw * GCSWEEPCOST; } - case GCSsweepudata: { - if (g->sweepfin) { - g->sweepfin = sweeplist(L, g->sweepfin, GCSWEEPMAX); - return GCSWEEPMAX*GCSWEEPCOST; - } - else { - g->gcstate = GCSsweep; - return 0; - } + case GCSswpallgc: { /* sweep "regular" objects */ + return sweepstep(L, g, GCSswpfinobj, &g->finobj); + } + case GCSswpfinobj: { /* sweep objects with finalizers */ + return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + } + case GCSswptobefnz: { /* sweep objects to be finalized */ + return sweepstep(L, g, GCSswpend, NULL); } - case GCSsweep: { - if (g->sweepgc) { - g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); - return GCSWEEPMAX*GCSWEEPCOST; + case GCSswpend: { /* finish sweeps */ + makewhite(g, g->mainthread); /* sweep main thread */ + checkSizes(L, g); + g->gcstate = GCScallfin; + return 0; + } + case GCScallfin: { /* call remaining finalizers */ + if (g->tobefnz && g->gckind != KGC_EMERGENCY) { + int n = runafewfinalizers(L); + return (n * GCFINALIZECOST); } - else { - /* sweep main thread */ - GCObject *mt = obj2gco(g->mainthread); - sweeplist(L, &mt, 1); - checkSizes(L); + else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ - return GCSWEEPCOST; + return 0; } } default: lua_assert(0); return 0; @@ -1122,105 +1118,70 @@ void luaC_runtilstate (lua_State *L, int statesmask) { } -static void generationalcollection (lua_State *L) { - global_State *g = G(L); - lua_assert(g->gcstate == GCSpropagate); - if (g->GCestimate == 0) { /* signal for another major collection? */ - luaC_fullgc(L, 0); /* perform a full regular collection */ - g->GCestimate = gettotalbytes(g); /* update control */ - } +/* +** get GC debt and convert it from Kb to 'work units' (avoid zero debt +** and overflows) +*/ +static l_mem getdebt (global_State *g) { + l_mem debt = g->GCdebt; + int stepmul = g->gcstepmul; + if (debt <= 0) return 0; /* minimal debt */ else { - lu_mem estimate = g->GCestimate; - luaC_runtilstate(L, bitmask(GCSpause)); /* run complete (minor) cycle */ - g->gcstate = GCSpropagate; /* skip restart */ - if (gettotalbytes(g) > (estimate / 100) * g->gcmajorinc) - g->GCestimate = 0; /* signal for a major collection */ - else - g->GCestimate = estimate; /* keep estimate from last major coll. */ - + debt = (debt / STEPMULADJ) + 1; + debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; + return debt; } - setpause(g, gettotalbytes(g)); - lua_assert(g->gcstate == GCSpropagate); } - -static void incstep (lua_State *L) { +/* +** performs a basic GC step when collector is running +*/ +void luaC_step (lua_State *L) { global_State *g = G(L); - l_mem debt = g->GCdebt; - int stepmul = g->gcstepmul; - if (stepmul < 40) stepmul = 40; /* avoid ridiculous low values (and 0) */ - /* convert debt from Kb to 'work units' (avoid zero debt and overflows) */ - debt = (debt / STEPMULADJ) + 1; - debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; - do { /* always perform at least one single step */ - lu_mem work = singlestep(L); /* do some work */ + l_mem debt = getdebt(g); /* GC deficit (be paid now) */ + if (!g->gcrunning) { /* not running? */ + luaE_setdebt(g, -GCSTEPSIZE * 10); /* avoid being called too often */ + return; + } + do { /* repeat until pause or enough "credit" (negative debt) */ + lu_mem work = singlestep(L); /* perform one single step */ debt -= work; } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause); if (g->gcstate == GCSpause) - setpause(g, g->GCestimate); /* pause until next cycle */ + setpause(g); /* pause until next cycle */ else { - debt = (debt / stepmul) * STEPMULADJ; /* convert 'work units' to Kb */ + debt = (debt / g->gcstepmul) * STEPMULADJ; /* convert 'work units' to Kb */ luaE_setdebt(g, debt); + runafewfinalizers(L); } } /* -** performs a basic GC step -*/ -void luaC_forcestep (lua_State *L) { - global_State *g = G(L); - int i; - if (isgenerational(g)) generationalcollection(L); - else incstep(L); - /* run a few finalizers (or all of them at the end of a collect cycle) */ - for (i = 0; g->tobefnz && (i < GCFINALIZENUM || g->gcstate == GCSpause); i++) - GCTM(L, 1); /* call one finalizer */ -} - - -/* -** performs a basic GC step only if collector is running -*/ -void luaC_step (lua_State *L) { - global_State *g = G(L); - if (g->gcrunning) luaC_forcestep(L); - else luaE_setdebt(g, -GCSTEPSIZE); /* avoid being called too often */ -} - - - -/* -** performs a full GC cycle; if "isemergency", does not call -** finalizers (which could change stack positions) +** Performs a full GC cycle; if 'isemergency', set a flag to avoid +** some operations which could change the interpreter state in some +** unexpected ways (running finalizers and shrinking some structures). +** Before running the collection, check 'keepinvariant'; if it is true, +** there may be some objects marked as black, so the collector has +** to sweep all objects to turn them back to white (as white has not +** changed, nothing will be collected). */ void luaC_fullgc (lua_State *L, int isemergency) { global_State *g = G(L); - int origkind = g->gckind; - lua_assert(origkind != KGC_EMERGENCY); - if (isemergency) /* do not run finalizers during emergency GC */ - g->gckind = KGC_EMERGENCY; - else { - g->gckind = KGC_NORMAL; - callallpendingfinalizers(L, 1); - } - if (keepinvariant(g)) { /* may there be some black objects? */ - /* must sweep all objects to turn them back to white - (as white has not changed, nothing will be collected) */ - entersweep(L); + lua_assert(g->gckind == KGC_NORMAL); + if (isemergency) g->gckind = KGC_EMERGENCY; /* set flag */ + if (keepinvariant(g)) { /* black objects? */ + entersweep(L); /* sweep everything to turn them back to white */ } /* finish any pending sweep phase to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, ~bitmask(GCSpause)); /* start new collection */ - luaC_runtilstate(L, bitmask(GCSpause)); /* run entire collection */ - if (origkind == KGC_GEN) { /* generational mode? */ - /* generational mode must be kept in propagate phase */ - luaC_runtilstate(L, bitmask(GCSpropagate)); - } - g->gckind = origkind; - setpause(g, gettotalbytes(g)); - if (!isemergency) /* do not run finalizers during emergency GC */ - callallpendingfinalizers(L, 1); + luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ + /* estimate must be correct after a full GC cycle */ + lua_assert(g->GCestimate == gettotalbytes(g)); + luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + g->gckind = KGC_NORMAL; + setpause(g); } /* }====================================================== */ diff --git a/deps/lua/lgc.h b/deps/lua/lgc.h index 84bb1cd..1775ca4 100644 --- a/deps/lua/lgc.h +++ b/deps/lua/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.58.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lgc.h,v 2.90 2015/10/21 18:15:15 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -38,36 +38,27 @@ */ #define GCSpropagate 0 #define GCSatomic 1 -#define GCSsweepstring 2 -#define GCSsweepudata 3 -#define GCSsweep 4 -#define GCSpause 5 +#define GCSswpallgc 2 +#define GCSswpfinobj 3 +#define GCSswptobefnz 4 +#define GCSswpend 5 +#define GCScallfin 6 +#define GCSpause 7 #define issweepphase(g) \ - (GCSsweepstring <= (g)->gcstate && (g)->gcstate <= GCSsweep) + (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) -#define isgenerational(g) ((g)->gckind == KGC_GEN) /* -** macros to tell when main invariant (white objects cannot point to black -** ones) must be kept. During a non-generational collection, the sweep +** macro to tell when main invariant (white objects cannot point to black +** ones) must be kept. During a collection, the sweep ** phase may break the invariant, as objects turned white may point to ** still-black objects. The invariant is restored when sweep ends and -** all objects are white again. During a generational collection, the -** invariant must be kept all times. +** all objects are white again. */ -#define keepinvariant(g) (isgenerational(g) || g->gcstate <= GCSatomic) - - -/* -** Outside the collector, the state in generational mode is kept in -** 'propagate', so 'keepinvariant' is always true. -*/ -#define keepinvariantout(g) \ - check_exp(g->gcstate == GCSpropagate || !isgenerational(g), \ - g->gcstate <= GCSatomic) +#define keepinvariant(g) ((g)->gcstate <= GCSatomic) /* @@ -83,75 +74,74 @@ #define testbit(x,b) testbits(x, bitmask(b)) -/* Layout for bit use in `marked' field: */ +/* Layout for bit use in 'marked' field: */ #define WHITE0BIT 0 /* object is white (type 0) */ #define WHITE1BIT 1 /* object is white (type 1) */ #define BLACKBIT 2 /* object is black */ -#define FINALIZEDBIT 3 /* object has been separated for finalization */ -#define SEPARATED 4 /* object is in 'finobj' list or in 'tobefnz' */ -#define FIXEDBIT 5 /* object is fixed (should not be collected) */ -#define OLDBIT 6 /* object is old (only in generational mode) */ +#define FINALIZEDBIT 3 /* object has been marked for finalization */ /* bit 7 is currently used by tests (luaL_checkmemory) */ #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) -#define iswhite(x) testbits((x)->gch.marked, WHITEBITS) -#define isblack(x) testbit((x)->gch.marked, BLACKBIT) +#define iswhite(x) testbits((x)->marked, WHITEBITS) +#define isblack(x) testbit((x)->marked, BLACKBIT) #define isgray(x) /* neither white nor black */ \ - (!testbits((x)->gch.marked, WHITEBITS | bitmask(BLACKBIT))) - -#define isold(x) testbit((x)->gch.marked, OLDBIT) + (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) -/* MOVE OLD rule: whenever an object is moved to the beginning of - a GC list, its old bit must be cleared */ -#define resetoldbit(o) resetbit((o)->gch.marked, OLDBIT) +#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) -#define otherwhite(g) (g->currentwhite ^ WHITEBITS) +#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) #define isdeadm(ow,m) (!(((m) ^ WHITEBITS) & (ow))) -#define isdead(g,v) isdeadm(otherwhite(g), (v)->gch.marked) +#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) -#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) -#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) - -#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) +#define changewhite(x) ((x)->marked ^= WHITEBITS) +#define gray2black(x) l_setbit((x)->marked, BLACKBIT) #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) -#define luaC_condGC(L,c) \ - {if (G(L)->GCdebt > 0) {c;}; condchangemem(L);} -#define luaC_checkGC(L) luaC_condGC(L, luaC_step(L);) +/* +** Does one step of collection when debt becomes positive. 'pre'/'pos' +** allows some adjustments to be done only when needed. macro +** 'condchangemem' is used only for heavy tests (forcing a full +** GC cycle on every opportunity) +*/ +#define luaC_condGC(L,pre,pos) \ + { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ + condchangemem(L,pre,pos); } +/* more often than not, 'pre'/'pos' are empty */ +#define luaC_checkGC(L) luaC_condGC(L,,) -#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ - luaC_barrier_(L,obj2gco(p),gcvalue(v)); } -#define luaC_barrierback(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ - luaC_barrierback_(L,p); } +#define luaC_barrier(L,p,v) ( \ + (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ + luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) -#define luaC_objbarrier(L,p,o) \ - { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ - luaC_barrier_(L,obj2gco(p),obj2gco(o)); } +#define luaC_barrierback(L,p,v) ( \ + (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ + luaC_barrierback_(L,p) : cast_void(0)) -#define luaC_objbarrierback(L,p,o) \ - { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) luaC_barrierback_(L,p); } +#define luaC_objbarrier(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? \ + luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) -#define luaC_barrierproto(L,p,c) \ - { if (isblack(obj2gco(p))) luaC_barrierproto_(L,p,c); } +#define luaC_upvalbarrier(L,uv) ( \ + (iscollectable((uv)->v) && !upisopen(uv)) ? \ + luaC_upvalbarrier_(L,uv) : cast_void(0)) +LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); -LUAI_FUNC void luaC_forcestep (lua_State *L); LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); -LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, - GCObject **list, int offset); +LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); -LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); -LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c); +LUAI_FUNC void luaC_barrierback_ (lua_State *L, Table *o); +LUAI_FUNC void luaC_upvalbarrier_ (lua_State *L, UpVal *uv); LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); -LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv); -LUAI_FUNC void luaC_changemode (lua_State *L, int mode); +LUAI_FUNC void luaC_upvdeccount (lua_State *L, UpVal *uv); + #endif diff --git a/deps/lua/linit.c b/deps/lua/linit.c index a8dc239..7d3b806 100644 --- a/deps/lua/linit.c +++ b/deps/lua/linit.c @@ -1,20 +1,33 @@ /* -** $Id: linit.c,v 1.32.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: linit.c,v 1.38 2015/01/05 13:48:33 roberto Exp $ ** Initialization of libraries for lua.c and other clients ** See Copyright Notice in lua.h */ +#define linit_c +#define LUA_LIB + /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a ** different set of libraries, copy this file to your project and edit ** it to suit your needs. +** +** You can also *preload* libraries, so that a later 'require' can +** open the library, which is already linked to the application. +** For that, do the following code: +** +** luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD"); +** lua_pushcfunction(L, luaopen_modname); +** lua_setfield(L, -2, modname); +** lua_pop(L, 1); // remove _PRELOAD table */ +#include "lprefix.h" -#define linit_c -#define LUA_LIB + +#include #include "lua.h" @@ -34,9 +47,12 @@ static const luaL_Reg loadedlibs[] = { {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, - {LUA_BITLIBNAME, luaopen_bit32}, {LUA_MATHLIBNAME, luaopen_math}, + {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, +#if defined(LUA_COMPAT_BITLIB) + {LUA_BITLIBNAME, luaopen_bit32}, +#endif #if defined(LUA_CACHELIB) {LUA_CACHELIB, luaopen_cache}, #endif @@ -44,27 +60,12 @@ static const luaL_Reg loadedlibs[] = { }; -/* -** these libs are preloaded and must be required before used -*/ -static const luaL_Reg preloadedlibs[] = { - {NULL, NULL} -}; - - LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib; - /* call open functions from 'loadedlibs' and set results to global table */ + /* "require" functions from 'loadedlibs' and set results to global table */ for (lib = loadedlibs; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } - /* add open functions from 'preloadedlibs' into 'package.preload' table */ - luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD"); - for (lib = preloadedlibs; lib->func; lib++) { - lua_pushcfunction(L, lib->func); - lua_setfield(L, -2, lib->name); - } - lua_pop(L, 1); /* remove _PRELOAD table */ } diff --git a/deps/lua/liolib.c b/deps/lua/liolib.c index 2a4ec4a..a91ba39 100644 --- a/deps/lua/liolib.c +++ b/deps/lua/liolib.c @@ -1,120 +1,139 @@ /* -** $Id: liolib.c,v 2.112.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: liolib.c,v 2.148 2015/11/23 11:36:11 roberto Exp $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ +#define liolib_c +#define LUA_LIB -/* -** This definition must come before the inclusion of 'stdio.h'; it -** should not affect non-POSIX systems -*/ -#if !defined(_FILE_OFFSET_BITS) -#define _LARGEFILE_SOURCE 1 -#define _FILE_OFFSET_BITS 64 -#endif +#include "lprefix.h" +#include #include +#include #include #include #include -#define liolib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" #include "lualib.h" -#if !defined(lua_checkmode) + /* -** Check whether 'mode' matches '[rwa]%+?b?'. ** Change this macro to accept other modes for 'fopen' besides ** the standard ones. */ -#define lua_checkmode(mode) \ +#if !defined(l_checkmode) + +/* accepted extensions to 'mode' in 'fopen' */ +#if !defined(L_MODEEXT) +#define L_MODEEXT "b" +#endif + +/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ +#define l_checkmode(mode) \ (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && \ - (*mode != '+' || ++mode) && /* skip if char is '+' */ \ - (*mode != 'b' || ++mode) && /* skip if char is 'b' */ \ - (*mode == '\0')) + (*mode != '+' || (++mode, 1)) && /* skip if char is '+' */ \ + (strspn(mode, L_MODEEXT) == strlen(mode))) #endif /* ** {====================================================== -** lua_popen spawns a new process connected to the current +** l_popen spawns a new process connected to the current ** one through the file streams. ** ======================================================= */ -#if !defined(lua_popen) /* { */ - -#if defined(LUA_USE_POPEN) /* { */ +#if !defined(l_popen) /* { */ -#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) -#define lua_pclose(L,file) ((void)L, pclose(file)) +#if defined(LUA_USE_POSIX) /* { */ -#elif defined(LUA_WIN) /* }{ */ +#define l_popen(L,c,m) (fflush(NULL), popen(c,m)) +#define l_pclose(L,file) (pclose(file)) -#define lua_popen(L,c,m) ((void)L, _popen(c,m)) -#define lua_pclose(L,file) ((void)L, _pclose(file)) +#elif defined(LUA_USE_WINDOWS) /* }{ */ +#define l_popen(L,c,m) (_popen(c,m)) +#define l_pclose(L,file) (_pclose(file)) #else /* }{ */ -#define lua_popen(L,c,m) ((void)((void)c, m), \ - luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) -#define lua_pclose(L,file) ((void)((void)L, file), -1) - +/* ISO C definitions */ +#define l_popen(L,c,m) \ + ((void)((void)c, m), \ + luaL_error(L, "'popen' not supported"), \ + (FILE*)0) +#define l_pclose(L,file) ((void)L, (void)file, -1) #endif /* } */ -#endif /* } */ +#endif /* } */ /* }====================================================== */ +#if !defined(l_getc) /* { */ + +#if defined(LUA_USE_POSIX) +#define l_getc(f) getc_unlocked(f) +#define l_lockfile(f) flockfile(f) +#define l_unlockfile(f) funlockfile(f) +#else +#define l_getc(f) getc(f) +#define l_lockfile(f) ((void)0) +#define l_unlockfile(f) ((void)0) +#endif + +#endif /* } */ + + /* ** {====================================================== -** lua_fseek: configuration for longer offsets +** l_fseek: configuration for longer offsets ** ======================================================= */ -#if !defined(lua_fseek) && !defined(LUA_ANSI) /* { */ +#if !defined(l_fseek) /* { */ #if defined(LUA_USE_POSIX) /* { */ +#include + #define l_fseek(f,o,w) fseeko(f,o,w) #define l_ftell(f) ftello(f) #define l_seeknum off_t -#elif defined(LUA_WIN) && !defined(_CRTIMP_TYPEINFO) \ +#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ -/* Windows (but not DDK) and Visual C++ 2005 or higher */ +/* Windows (but not DDK) and Visual C++ 2005 or higher */ #define l_fseek(f,o,w) _fseeki64(f,o,w) #define l_ftell(f) _ftelli64(f) #define l_seeknum __int64 -#endif /* } */ - -#endif /* } */ - +#else /* }{ */ -#if !defined(l_fseek) /* default definitions */ +/* ISO C definitions */ #define l_fseek(f,o,w) fseek(f,o,w) #define l_ftell(f) ftell(f) #define l_seeknum long -#endif + +#endif /* } */ + +#endif /* } */ /* }====================================================== */ #define IO_PREFIX "_IO_" +#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) #define IO_INPUT (IO_PREFIX "input") #define IO_OUTPUT (IO_PREFIX "output") @@ -161,9 +180,9 @@ static FILE *tofile (lua_State *L) { /* -** When creating file handles, always creates a `closed' file handle +** When creating file handles, always creates a 'closed' file handle ** before opening the actual file; so, if there is a memory error, the -** file is not left opened. +** handle is in a consistent state. */ static LStream *newprefile (lua_State *L) { LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream)); @@ -173,9 +192,14 @@ static LStream *newprefile (lua_State *L) { } +/* +** Calls the 'close' function from a file handle. The 'volatile' avoids +** a bug in some versions of the Clang compiler (e.g., clang 3.0 for +** 32 bits). +*/ static int aux_close (lua_State *L) { LStream *p = tolstream(L); - lua_CFunction cf = p->closef; + volatile lua_CFunction cf = p->closef; p->closef = NULL; /* mark stream as closed */ return (*cf)(L); /* close it */ } @@ -219,7 +243,7 @@ static void opencheck (lua_State *L, const char *fname, const char *mode) { LStream *p = newfile(L); p->f = fopen(fname, mode); if (p->f == NULL) - luaL_error(L, "cannot open file " LUA_QS " (%s)", fname, strerror(errno)); + luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); } @@ -228,7 +252,7 @@ static int io_open (lua_State *L) { const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newfile(L); const char *md = mode; /* to traverse/check mode */ - luaL_argcheck(L, lua_checkmode(md), 2, "invalid mode"); + luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); p->f = fopen(filename, mode); return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } @@ -239,7 +263,7 @@ static int io_open (lua_State *L) { */ static int io_pclose (lua_State *L) { LStream *p = tolstream(L); - return luaL_execresult(L, lua_pclose(L, p->f)); + return luaL_execresult(L, l_pclose(L, p->f)); } @@ -247,7 +271,7 @@ static int io_popen (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newprefile(L); - p->f = lua_popen(L, filename, mode); + p->f = l_popen(L, filename, mode); p->closef = &io_pclose; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } @@ -265,7 +289,7 @@ static FILE *getiofile (lua_State *L, const char *findex) { lua_getfield(L, LUA_REGISTRYINDEX, findex); p = (LStream *)lua_touserdata(L, -1); if (isclosed(p)) - luaL_error(L, "standard %s file is closed", findex + strlen(IO_PREFIX)); + luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN); return p->f; } @@ -300,15 +324,18 @@ static int io_output (lua_State *L) { static int io_readline (lua_State *L); +/* +** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit +** in the limit for upvalues of a closure) +*/ +#define MAXARGLINE 250 + static void aux_lines (lua_State *L, int toclose) { - int i; int n = lua_gettop(L) - 1; /* number of arguments to read */ - /* ensure that arguments will fit here and into 'io_readline' stack */ - luaL_argcheck(L, n <= LUA_MINSTACK - 3, LUA_MINSTACK - 3, "too many options"); - lua_pushvalue(L, 1); /* file handle */ + luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); lua_pushinteger(L, n); /* number of arguments to read */ lua_pushboolean(L, toclose); /* close/not close file when finished */ - for (i = 1; i <= n; i++) lua_pushvalue(L, i + 1); /* copy arguments */ + lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */ lua_pushcclosure(L, io_readline, 3 + n); } @@ -347,13 +374,88 @@ static int io_lines (lua_State *L) { */ -static int read_number (lua_State *L, FILE *f) { - lua_Number d; - if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { - lua_pushnumber(L, d); - return 1; +/* maximum length of a numeral */ +#define MAXRN 200 + +/* auxiliary structure used by 'read_number' */ +typedef struct { + FILE *f; /* file being read */ + int c; /* current character (look ahead) */ + int n; /* number of elements in buffer 'buff' */ + char buff[MAXRN + 1]; /* +1 for ending '\0' */ +} RN; + + +/* +** Add current char to buffer (if not out of space) and read next one +*/ +static int nextc (RN *rn) { + if (rn->n >= MAXRN) { /* buffer overflow? */ + rn->buff[0] = '\0'; /* invalidate result */ + return 0; /* fail */ } else { + rn->buff[rn->n++] = rn->c; /* save current char */ + rn->c = l_getc(rn->f); /* read next one */ + return 1; + } +} + + +/* +** Accept current char if it is in 'set' (of size 1 or 2) +*/ +static int test2 (RN *rn, const char *set) { + if (rn->c == set[0] || (rn->c == set[1] && rn->c != '\0')) + return nextc(rn); + else return 0; +} + + +/* +** Read a sequence of (hex)digits +*/ +static int readdigits (RN *rn, int hex) { + int count = 0; + while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) + count++; + return count; +} + + +/* +** Read a number: first reads a valid prefix of a numeral into a buffer. +** Then it calls 'lua_stringtonumber' to check whether the format is +** correct and to convert it to a Lua number +*/ +static int read_number (lua_State *L, FILE *f) { + RN rn; + int count = 0; + int hex = 0; + char decp[2]; + rn.f = f; rn.n = 0; + decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ + decp[1] = '\0'; + l_lockfile(rn.f); + do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ + test2(&rn, "-+"); /* optional signal */ + if (test2(&rn, "0")) { + if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ + else count = 1; /* count initial '0' as a valid digit */ + } + count += readdigits(&rn, hex); /* integral part */ + if (test2(&rn, decp)) /* decimal point? */ + count += readdigits(&rn, hex); /* fractional part */ + if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ + test2(&rn, "-+"); /* exponent signal */ + readdigits(&rn, 0); /* exponent digits */ + } + ungetc(rn.c, rn.f); /* unread look-ahead char */ + l_unlockfile(rn.f); + rn.buff[rn.n] = '\0'; /* finish string */ + if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */ + return 1; /* ok */ + else { /* invalid format */ lua_pushnil(L); /* "result" to be removed */ return 0; /* read fails */ } @@ -362,48 +464,42 @@ static int read_number (lua_State *L, FILE *f) { static int test_eof (lua_State *L, FILE *f) { int c = getc(f); - ungetc(c, f); - lua_pushlstring(L, NULL, 0); + ungetc(c, f); /* no-op when c == EOF */ + lua_pushliteral(L, ""); return (c != EOF); } static int read_line (lua_State *L, FILE *f, int chop) { luaL_Buffer b; + int c = '\0'; luaL_buffinit(L, &b); - for (;;) { - size_t l; - char *p = luaL_prepbuffer(&b); - if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ - luaL_pushresult(&b); /* close buffer */ - return (lua_rawlen(L, -1) > 0); /* check whether read something */ - } - l = strlen(p); - if (l == 0 || p[l-1] != '\n') - luaL_addsize(&b, l); - else { - luaL_addsize(&b, l - chop); /* chop 'eol' if needed */ - luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ - } + while (c != EOF && c != '\n') { /* repeat until end of line */ + char *buff = luaL_prepbuffer(&b); /* preallocate buffer */ + int i = 0; + l_lockfile(f); /* no memory errors can happen inside the lock */ + while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') + buff[i++] = c; + l_unlockfile(f); + luaL_addsize(&b, i); } + if (!chop && c == '\n') /* want a newline and have one? */ + luaL_addchar(&b, c); /* add ending newline to result */ + luaL_pushresult(&b); /* close buffer */ + /* return ok if read something (either a newline or something else) */ + return (c == '\n' || lua_rawlen(L, -1) > 0); } -#define MAX_SIZE_T (~(size_t)0) - static void read_all (lua_State *L, FILE *f) { - size_t rlen = LUAL_BUFFERSIZE; /* how much to read in each cycle */ + size_t nr; luaL_Buffer b; luaL_buffinit(L, &b); - for (;;) { - char *p = luaL_prepbuffsize(&b, rlen); - size_t nr = fread(p, sizeof(char), rlen, f); + do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ + char *p = luaL_prepbuffer(&b); + nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); luaL_addsize(&b, nr); - if (nr < rlen) break; /* eof? */ - else if (rlen <= (MAX_SIZE_T / 4)) /* avoid buffers too large */ - rlen *= 2; /* double buffer size at each iteration */ - } + } while (nr == LUAL_BUFFERSIZE); luaL_pushresult(&b); /* close buffer */ } @@ -435,13 +531,13 @@ static int g_read (lua_State *L, FILE *f, int first) { success = 1; for (n = first; nargs-- && success; n++) { if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tointeger(L, n); + size_t l = (size_t)luaL_checkinteger(L, n); success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); } else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { + const char *p = luaL_checkstring(L, n); + if (*p == '*') p++; /* skip optional '*' (for compatibility) */ + switch (*p) { case 'n': /* number */ success = read_number(L, f); break; @@ -488,11 +584,12 @@ static int io_readline (lua_State *L) { if (isclosed(p)) /* file is already closed? */ return luaL_error(L, "file is already closed"); lua_settop(L , 1); + luaL_checkstack(L, n, "too many arguments"); for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ lua_pushvalue(L, lua_upvalueindex(3 + i)); n = g_read(L, p->f, 2); /* 'n' is number of results */ lua_assert(n > 0); /* should return at least a nil */ - if (!lua_isnil(L, -n)) /* read at least one value? */ + if (lua_toboolean(L, -n)) /* read at least one value? */ return n; /* return them */ else { /* first result is nil: EOF or error */ if (n > 1) { /* is there error information? */ @@ -517,8 +614,10 @@ static int g_write (lua_State *L, FILE *f, int arg) { for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ - status = status && - fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + int len = lua_isinteger(L, arg) + ? fprintf(f, LUA_INTEGER_FMT, lua_tointeger(L, arg)) + : fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)); + status = status && (len > 0); } else { size_t l; @@ -548,15 +647,15 @@ static int f_seek (lua_State *L) { static const char *const modenames[] = {"set", "cur", "end", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, "cur", modenames); - lua_Number p3 = luaL_optnumber(L, 3, 0); + lua_Integer p3 = luaL_optinteger(L, 3, 0); l_seeknum offset = (l_seeknum)p3; - luaL_argcheck(L, (lua_Number)offset == p3, 3, + luaL_argcheck(L, (lua_Integer)offset == p3, 3, "not an integer in proper range"); op = l_fseek(f, offset, mode[op]); if (op) return luaL_fileresult(L, 0, NULL); /* error */ else { - lua_pushnumber(L, (lua_Number)l_ftell(f)); + lua_pushinteger(L, (lua_Integer)l_ftell(f)); return 1; } } @@ -568,7 +667,7 @@ static int f_setvbuf (lua_State *L) { FILE *f = tofile(L); int op = luaL_checkoption(L, 2, NULL, modenames); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); - int res = setvbuf(f, NULL, mode[op], sz); + int res = setvbuf(f, NULL, mode[op], (size_t)sz); return luaL_fileresult(L, res == 0, NULL); } diff --git a/deps/lua/llex.c b/deps/lua/llex.c index c4b820e..16ea3eb 100644 --- a/deps/lua/llex.c +++ b/deps/lua/llex.c @@ -1,20 +1,24 @@ /* -** $Id: llex.c,v 2.63.1.2 2013/08/30 15:49:41 roberto Exp $ +** $Id: llex.c,v 2.95 2015/11/19 19:16:22 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ +#define llex_c +#define LUA_CORE + +#include "lprefix.h" + #include #include -#define llex_c -#define LUA_CORE - #include "lua.h" #include "lctype.h" +#include "ldebug.h" #include "ldo.h" +#include "lgc.h" #include "llex.h" #include "lobject.h" #include "lparser.h" @@ -38,8 +42,9 @@ static const char *const luaX_tokens [] = { "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", - "..", "...", "==", ">=", "<=", "~=", "::", "", - "", "", "" + "//", "..", "...", "==", ">=", "<=", "~=", + "<<", ">>", "::", "", + "", "", "", "" }; @@ -53,7 +58,7 @@ static void save (LexState *ls, int c) { Mbuffer *b = ls->buff; if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { size_t newsize; - if (luaZ_sizebuffer(b) >= MAX_SIZET/2) + if (luaZ_sizebuffer(b) >= MAX_SIZE/2) lexerror(ls, "lexical element too long", 0); newsize = luaZ_sizebuffer(b) * 2; luaZ_resizebuffer(ls->L, b, newsize); @@ -64,24 +69,25 @@ static void save (LexState *ls, int c) { void luaX_init (lua_State *L) { int i; + TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */ + luaC_fix(L, obj2gco(e)); /* never collect this name */ for (i=0; itsv.extra = cast_byte(i+1); /* reserved word */ + luaC_fix(L, obj2gco(ts)); /* reserved words are never collected */ + ts->extra = cast_byte(i+1); /* reserved word */ } } const char *luaX_token2str (LexState *ls, int token) { if (token < FIRST_RESERVED) { /* single-byte symbols? */ - lua_assert(token == cast(unsigned char, token)); - return (lisprint(token)) ? luaO_pushfstring(ls->L, LUA_QL("%c"), token) : - luaO_pushfstring(ls->L, "char(%d)", token); + lua_assert(token == cast_uchar(token)); + return luaO_pushfstring(ls->L, "'%c'", token); } else { const char *s = luaX_tokens[token - FIRST_RESERVED]; if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ - return luaO_pushfstring(ls->L, LUA_QS, s); + return luaO_pushfstring(ls->L, "'%s'", s); else /* names, strings, and numerals */ return s; } @@ -90,11 +96,10 @@ const char *luaX_token2str (LexState *ls, int token) { static const char *txtToken (LexState *ls, int token) { switch (token) { - case TK_NAME: - case TK_STRING: - case TK_NUMBER: + case TK_NAME: case TK_STRING: + case TK_FLT: case TK_INT: save(ls, '\0'); - return luaO_pushfstring(ls->L, LUA_QS, luaZ_buffer(ls->buff)); + return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); default: return luaX_token2str(ls, token); } @@ -102,9 +107,7 @@ static const char *txtToken (LexState *ls, int token) { static l_noret lexerror (LexState *ls, const char *msg, int token) { - char buff[LUA_IDSIZE]; - luaO_chunkid(buff, getstr(ls->source), LUA_IDSIZE); - msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); + msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber); if (token) luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); luaD_throw(ls->L, LUA_ERRSYNTAX); @@ -117,24 +120,24 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) { /* -** creates a new string and anchors it in function's table so that -** it will not be collected until the end of the function's compilation -** (by that time it should be anchored in function's prototype) +** creates a new string and anchors it in scanner's table so that +** it will not be collected until the end of the compilation +** (by that time it should be anchored somewhere) */ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { lua_State *L = ls->L; - TValue *o; /* entry for `str' */ + TValue *o; /* entry for 'str' */ TString *ts = luaS_newlstr(L, str, l); /* create new string */ setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */ - o = luaH_set(L, ls->fs->h, L->top - 1); - if (ttisnil(o)) { /* not in use yet? (see 'addK') */ + o = luaH_set(L, ls->h, L->top - 1); + if (ttisnil(o)) { /* not in use yet? */ /* boolean value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ setbvalue(o, 1); /* t[string] = true */ luaC_checkGC(L); } else { /* string already present */ - ts = rawtsvalue(keyfromval(o)); /* re-use value previously stored */ + ts = tsvalue(keyfromval(o)); /* re-use value previously stored */ } L->top--; /* remove string from stack */ return ts; @@ -148,16 +151,17 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { static void inclinenumber (LexState *ls) { int old = ls->current; lua_assert(currIsNewline(ls)); - next(ls); /* skip `\n' or `\r' */ + next(ls); /* skip '\n' or '\r' */ if (currIsNewline(ls) && ls->current != old) - next(ls); /* skip `\n\r' or `\r\n' */ + next(ls); /* skip '\n\r' or '\r\n' */ if (++ls->linenumber >= MAX_INT) - luaX_syntaxerror(ls, "chunk has too many lines"); + lexerror(ls, "chunk has too many lines", 0); } void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, int firstchar) { + ls->t.token = 0; ls->decpoint = '.'; ls->L = L; ls->current = firstchar; @@ -167,8 +171,7 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, ls->linenumber = 1; ls->lastline = 1; ls->source = source; - ls->envn = luaS_new(L, LUA_ENV); /* create env name */ - luaS_fix(ls->envn); /* never collect this name */ + ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ } @@ -181,12 +184,26 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, */ +static int check_next1 (LexState *ls, int c) { + if (ls->current == c) { + next(ls); + return 1; + } + else return 0; +} + -static int check_next (LexState *ls, const char *set) { - if (ls->current == '\0' || !strchr(set, ls->current)) - return 0; - save_and_next(ls); - return 1; +/* +** Check whether current char is in set 'set' (with two chars) and +** saves it +*/ +static int check_next2 (LexState *ls, const char *set) { + lua_assert(set[2] == '\0'); + if (ls->current == set[0] || ls->current == set[1]) { + save_and_next(ls); + return 1; + } + else return 0; } @@ -194,65 +211,73 @@ static int check_next (LexState *ls, const char *set) { ** change all characters 'from' in buffer to 'to' */ static void buffreplace (LexState *ls, char from, char to) { - size_t n = luaZ_bufflen(ls->buff); - char *p = luaZ_buffer(ls->buff); - while (n--) - if (p[n] == from) p[n] = to; + if (from != to) { + size_t n = luaZ_bufflen(ls->buff); + char *p = luaZ_buffer(ls->buff); + while (n--) + if (p[n] == from) p[n] = to; + } } -#if !defined(getlocaledecpoint) -#define getlocaledecpoint() (localeconv()->decimal_point[0]) -#endif - - -#define buff2d(b,e) luaO_str2d(luaZ_buffer(b), luaZ_bufflen(b) - 1, e) - /* ** in case of format error, try to change decimal point separator to ** the one defined in the current locale and check again */ -static void trydecpoint (LexState *ls, SemInfo *seminfo) { +static void trydecpoint (LexState *ls, TValue *o) { char old = ls->decpoint; - ls->decpoint = getlocaledecpoint(); + ls->decpoint = lua_getlocaledecpoint(); buffreplace(ls, old, ls->decpoint); /* try new decimal separator */ - if (!buff2d(ls->buff, &seminfo->r)) { + if (luaO_str2num(luaZ_buffer(ls->buff), o) == 0) { /* format error with correct decimal point: no more options */ buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ - lexerror(ls, "malformed number", TK_NUMBER); + lexerror(ls, "malformed number", TK_FLT); } } /* LUA_NUMBER */ /* -** this function is quite liberal in what it accepts, as 'luaO_str2d' +** this function is quite liberal in what it accepts, as 'luaO_str2num' ** will reject ill-formed numerals. */ -static void read_numeral (LexState *ls, SemInfo *seminfo) { +static int read_numeral (LexState *ls, SemInfo *seminfo) { + TValue obj; const char *expo = "Ee"; int first = ls->current; lua_assert(lisdigit(ls->current)); save_and_next(ls); - if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */ + if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */ expo = "Pp"; for (;;) { - if (check_next(ls, expo)) /* exponent part? */ - check_next(ls, "+-"); /* optional exponent sign */ - if (lisxdigit(ls->current) || ls->current == '.') + if (check_next2(ls, expo)) /* exponent part? */ + check_next2(ls, "-+"); /* optional exponent sign */ + if (lisxdigit(ls->current)) + save_and_next(ls); + else if (ls->current == '.') save_and_next(ls); - else break; + else break; } save(ls, '\0'); buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ - if (!buff2d(ls->buff, &seminfo->r)) /* format error? */ - trydecpoint(ls, seminfo); /* try to update decimal point separator */ + if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ + trydecpoint(ls, &obj); /* try to update decimal point separator */ + if (ttisinteger(&obj)) { + seminfo->i = ivalue(&obj); + return TK_INT; + } + else { + lua_assert(ttisfloat(&obj)); + seminfo->r = fltvalue(&obj); + return TK_FLT; + } } /* -** skip a sequence '[=*[' or ']=*]' and return its number of '='s or -** -1 if sequence is malformed +** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return +** its number of '='s; otherwise, return a negative number (-1 iff there +** are no '='s after initial bracket) */ static int skip_sep (LexState *ls) { int count = 0; @@ -268,18 +293,22 @@ static int skip_sep (LexState *ls) { static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { - save_and_next(ls); /* skip 2nd `[' */ + int line = ls->linenumber; /* initial line (for error message) */ + save_and_next(ls); /* skip 2nd '[' */ if (currIsNewline(ls)) /* string starts with a newline? */ inclinenumber(ls); /* skip it */ for (;;) { switch (ls->current) { - case EOZ: - lexerror(ls, (seminfo) ? "unfinished long string" : - "unfinished long comment", TK_EOS); + case EOZ: { /* error */ + const char *what = (seminfo ? "string" : "comment"); + const char *msg = luaO_pushfstring(ls->L, + "unfinished long %s (starting at line %d)", what, line); + lexerror(ls, msg, TK_EOS); break; /* to avoid warnings */ + } case ']': { if (skip_sep(ls) == sep) { - save_and_next(ls); /* skip 2nd `]' */ + save_and_next(ls); /* skip 2nd ']' */ goto endloop; } break; @@ -302,40 +331,65 @@ static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { } -static void escerror (LexState *ls, int *c, int n, const char *msg) { - int i; - luaZ_resetbuffer(ls->buff); /* prepare error message */ - save(ls, '\\'); - for (i = 0; i < n && c[i] != EOZ; i++) - save(ls, c[i]); - lexerror(ls, msg, TK_STRING); +static void esccheck (LexState *ls, int c, const char *msg) { + if (!c) { + if (ls->current != EOZ) + save_and_next(ls); /* add current to buffer for error message */ + lexerror(ls, msg, TK_STRING); + } +} + + +static int gethexa (LexState *ls) { + save_and_next(ls); + esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected"); + return luaO_hexavalue(ls->current); } static int readhexaesc (LexState *ls) { - int c[3], i; /* keep input for error message */ - int r = 0; /* result accumulator */ - c[0] = 'x'; /* for error message */ - for (i = 1; i < 3; i++) { /* read two hexadecimal digits */ - c[i] = next(ls); - if (!lisxdigit(c[i])) - escerror(ls, c, i + 1, "hexadecimal digit expected"); - r = (r << 4) + luaO_hexavalue(c[i]); + int r = gethexa(ls); + r = (r << 4) + gethexa(ls); + luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */ + return r; +} + + +static unsigned long readutf8esc (LexState *ls) { + unsigned long r; + int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ + save_and_next(ls); /* skip 'u' */ + esccheck(ls, ls->current == '{', "missing '{'"); + r = gethexa(ls); /* must have at least one digit */ + while ((save_and_next(ls), lisxdigit(ls->current))) { + i++; + r = (r << 4) + luaO_hexavalue(ls->current); + esccheck(ls, r <= 0x10FFFF, "UTF-8 value too large"); } + esccheck(ls, ls->current == '}', "missing '}'"); + next(ls); /* skip '}' */ + luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */ return r; } +static void utf8esc (LexState *ls) { + char buff[UTF8BUFFSZ]; + int n = luaO_utf8esc(buff, readutf8esc(ls)); + for (; n > 0; n--) /* add 'buff' to string */ + save(ls, buff[UTF8BUFFSZ - n]); +} + + static int readdecesc (LexState *ls) { - int c[3], i; + int i; int r = 0; /* result accumulator */ for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ - c[i] = ls->current; - r = 10*r + c[i] - '0'; - next(ls); + r = 10*r + ls->current - '0'; + save_and_next(ls); } - if (r > UCHAR_MAX) - escerror(ls, c, i, "decimal escape too large"); + esccheck(ls, r <= UCHAR_MAX, "decimal escape too large"); + luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */ return r; } @@ -353,7 +407,7 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) { break; /* to avoid warnings */ case '\\': { /* escape sequences */ int c; /* final character to be saved */ - next(ls); /* do not save the `\' */ + save_and_next(ls); /* keep '\\' for error messages */ switch (ls->current) { case 'a': c = '\a'; goto read_save; case 'b': c = '\b'; goto read_save; @@ -363,12 +417,14 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) { case 't': c = '\t'; goto read_save; case 'v': c = '\v'; goto read_save; case 'x': c = readhexaesc(ls); goto read_save; + case 'u': utf8esc(ls); goto no_save; case '\n': case '\r': inclinenumber(ls); c = '\n'; goto only_save; case '\\': case '\"': case '\'': c = ls->current; goto read_save; case EOZ: goto no_save; /* will raise an error next loop */ case 'z': { /* zap following span of spaces */ + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ next(ls); /* skip the 'z' */ while (lisspace(ls->current)) { if (currIsNewline(ls)) inclinenumber(ls); @@ -377,15 +433,18 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) { goto no_save; } default: { - if (!lisdigit(ls->current)) - escerror(ls, &ls->current, 1, "invalid escape sequence"); - /* digital escape \ddd */ - c = readdecesc(ls); + esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); + c = readdecesc(ls); /* digital escape '\ddd' */ goto only_save; } } - read_save: next(ls); /* read next character */ - only_save: save(ls, c); /* save 'c' */ + read_save: + next(ls); + /* go through */ + only_save: + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + save(ls, c); + /* go through */ no_save: break; } default: @@ -417,7 +476,7 @@ static int llex (LexState *ls, SemInfo *seminfo) { next(ls); if (ls->current == '[') { /* long comment? */ int sep = skip_sep(ls); - luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ + luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ if (sep >= 0) { read_long_string(ls, NULL, sep); /* skip long comment */ luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ @@ -435,33 +494,41 @@ static int llex (LexState *ls, SemInfo *seminfo) { read_long_string(ls, seminfo, sep); return TK_STRING; } - else if (sep == -1) return '['; - else lexerror(ls, "invalid long string delimiter", TK_STRING); + else if (sep != -1) /* '[=...' missing second bracket */ + lexerror(ls, "invalid long string delimiter", TK_STRING); + return '['; } case '=': { next(ls); - if (ls->current != '=') return '='; - else { next(ls); return TK_EQ; } + if (check_next1(ls, '=')) return TK_EQ; + else return '='; } case '<': { next(ls); - if (ls->current != '=') return '<'; - else { next(ls); return TK_LE; } + if (check_next1(ls, '=')) return TK_LE; + else if (check_next1(ls, '<')) return TK_SHL; + else return '<'; } case '>': { next(ls); - if (ls->current != '=') return '>'; - else { next(ls); return TK_GE; } + if (check_next1(ls, '=')) return TK_GE; + else if (check_next1(ls, '>')) return TK_SHR; + else return '>'; + } + case '/': { + next(ls); + if (check_next1(ls, '/')) return TK_IDIV; + else return '/'; } case '~': { next(ls); - if (ls->current != '=') return '~'; - else { next(ls); return TK_NE; } + if (check_next1(ls, '=')) return TK_NE; + else return '~'; } case ':': { next(ls); - if (ls->current != ':') return ':'; - else { next(ls); return TK_DBCOLON; } + if (check_next1(ls, ':')) return TK_DBCOLON; + else return ':'; } case '"': case '\'': { /* short literal strings */ read_string(ls, ls->current, seminfo); @@ -469,18 +536,17 @@ static int llex (LexState *ls, SemInfo *seminfo) { } case '.': { /* '.', '..', '...', or number */ save_and_next(ls); - if (check_next(ls, ".")) { - if (check_next(ls, ".")) + if (check_next1(ls, '.')) { + if (check_next1(ls, '.')) return TK_DOTS; /* '...' */ else return TK_CONCAT; /* '..' */ } else if (!lisdigit(ls->current)) return '.'; - /* else go through */ + else return read_numeral(ls, seminfo); } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { - read_numeral(ls, seminfo); - return TK_NUMBER; + return read_numeral(ls, seminfo); } case EOZ: { return TK_EOS; @@ -495,7 +561,7 @@ static int llex (LexState *ls, SemInfo *seminfo) { luaZ_bufflen(ls->buff)); seminfo->ts = ts; if (isreserved(ts)) /* reserved word? */ - return ts->tsv.extra - 1 + FIRST_RESERVED; + return ts->extra - 1 + FIRST_RESERVED; else { return TK_NAME; } diff --git a/deps/lua/llex.h b/deps/lua/llex.h index a4acdd3..afb40b5 100644 --- a/deps/lua/llex.h +++ b/deps/lua/llex.h @@ -1,5 +1,5 @@ /* -** $Id: llex.h,v 1.72.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: llex.h,v 1.78 2014/10/29 15:38:24 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ @@ -14,6 +14,10 @@ #define FIRST_RESERVED 257 +#if !defined(LUA_ENV) +#define LUA_ENV "_ENV" +#endif + /* * WARNING: if you change the order of this enumeration, @@ -26,8 +30,10 @@ enum RESERVED { TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ - TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_DBCOLON, TK_EOS, - TK_NUMBER, TK_NAME, TK_STRING + TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, + TK_SHL, TK_SHR, + TK_DBCOLON, TK_EOS, + TK_FLT, TK_INT, TK_NAME, TK_STRING }; /* number of reserved words */ @@ -36,6 +42,7 @@ enum RESERVED { typedef union { lua_Number r; + lua_Integer i; TString *ts; } SemInfo; /* semantics information */ @@ -51,13 +58,14 @@ typedef struct Token { typedef struct LexState { int current; /* current character (charint) */ int linenumber; /* input line counter */ - int lastline; /* line of last token `consumed' */ + int lastline; /* line of last token 'consumed' */ Token t; /* current token */ Token lookahead; /* look ahead token */ struct FuncState *fs; /* current function (parser) */ struct lua_State *L; ZIO *z; /* input stream */ Mbuffer *buff; /* buffer for tokens */ + Table *h; /* to avoid collection/reuse strings */ struct Dyndata *dyd; /* dynamic structures used by the parser */ TString *source; /* current source name */ TString *envn; /* environment variable name */ diff --git a/deps/lua/llimits.h b/deps/lua/llimits.h index 152dd05..f21377f 100644 --- a/deps/lua/llimits.h +++ b/deps/lua/llimits.h @@ -1,6 +1,6 @@ /* -** $Id: llimits.h,v 1.103.1.1 2013/04/12 18:48:47 roberto Exp $ -** Limits, basic types, and some other `installation-dependent' definitions +** $Id: llimits.h,v 1.141 2015/11/19 19:16:22 roberto Exp $ +** Limits, basic types, and some other 'installation-dependent' definitions ** See Copyright Notice in lua.h */ @@ -14,54 +14,77 @@ #include "lua.h" - -typedef unsigned LUA_INT32 lu_int32; - +/* +** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count +** the total memory used by Lua (in bytes). Usually, 'size_t' and +** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. +*/ +#if defined(LUAI_MEM) /* { external definitions? */ typedef LUAI_UMEM lu_mem; - typedef LUAI_MEM l_mem; +#elif LUAI_BITSINT >= 32 /* }{ */ +typedef size_t lu_mem; +typedef ptrdiff_t l_mem; +#else /* 16-bit ints */ /* }{ */ +typedef unsigned long lu_mem; +typedef long l_mem; +#endif /* } */ - -/* chars used as small naturals (so that `char' is reserved for characters) */ +/* chars used as small naturals (so that 'char' is reserved for characters) */ typedef unsigned char lu_byte; -#define MAX_SIZET ((size_t)(~(size_t)0)-2) +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +/* maximum size visible for Lua (must be representable in a lua_Integer */ +#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ + : (size_t)(LUA_MAXINTEGER)) + -#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)) -#define MAX_LMEM ((l_mem) ((MAX_LUMEM >> 1) - 2)) +#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1)) -#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ +#define MAX_INT INT_MAX /* maximum value of an int */ + /* -** conversion of pointer to integer +** conversion of pointer to unsigned integer: ** this is for hashing only; there is no problem if the integer ** cannot hold the whole pointer value */ -#define IntPoint(p) ((unsigned int)(lu_mem)(p)) +#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX)) /* type to ensure maximum alignment */ -#if !defined(LUAI_USER_ALIGNMENT_T) -#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } +#if defined(LUAI_USER_ALIGNMENT_T) +typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; +#else +typedef union { + lua_Number n; + double u; + void *s; + lua_Integer i; + long l; +} L_Umaxalign; #endif -typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; -/* result of a `usual argument conversion' over lua_Number */ +/* types of 'usual argument conversions' for lua_Number and lua_Integer */ typedef LUAI_UACNUMBER l_uacNumber; +typedef LUAI_UACINT l_uacInt; /* internal assertions for in-house debugging */ #if defined(lua_assert) #define check_exp(c,e) (lua_assert(c), (e)) /* to avoid problems with conditions too long */ -#define lua_longassert(c) { if (!(c)) lua_assert(0); } +#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) #else #define lua_assert(c) ((void)0) #define check_exp(c,e) (e) @@ -72,38 +95,49 @@ typedef LUAI_UACNUMBER l_uacNumber; ** assertion for checking API calls */ #if !defined(luai_apicheck) - -#if defined(LUA_USE_APICHECK) -#include -#define luai_apicheck(L,e) assert(e) -#else -#define luai_apicheck(L,e) lua_assert(e) -#endif - +#define luai_apicheck(l,e) lua_assert(e) #endif #define api_check(l,e,msg) luai_apicheck(l,(e) && msg) +/* macro to avoid warnings about unused variables */ #if !defined(UNUSED) -#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#define UNUSED(x) ((void)(x)) #endif +/* type casts (a macro highlights casts in the code) */ #define cast(t, exp) ((t)(exp)) +#define cast_void(i) cast(void, (i)) #define cast_byte(i) cast(lu_byte, (i)) #define cast_num(i) cast(lua_Number, (i)) #define cast_int(i) cast(int, (i)) #define cast_uchar(i) cast(unsigned char, (i)) +/* cast a signed lua_Integer to lua_Unsigned */ +#if !defined(l_castS2U) +#define l_castS2U(i) ((lua_Unsigned)(i)) +#endif + +/* +** cast a lua_Unsigned to a signed lua_Integer; this cast is +** not strict ISO C, but two-complement architectures should +** work fine. +*/ +#if !defined(l_castU2S) +#define l_castU2S(i) ((lua_Integer)(i)) +#endif + + /* ** non-return type */ #if defined(__GNUC__) #define l_noret void __attribute__((noreturn)) -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && _MSC_VER >= 1200 #define l_noret void __declspec(noreturn) #else #define l_noret void @@ -119,29 +153,50 @@ typedef LUAI_UACNUMBER l_uacNumber; #define LUAI_MAXCCALLS 200 #endif -/* -** maximum number of upvalues in a closure (both C and Lua). (Value -** must fit in an unsigned char.) -*/ -#define MAXUPVAL UCHAR_MAX /* -** type for virtual-machine instructions +** type for virtual-machine instructions; ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) */ -typedef lu_int32 Instruction; - +#if LUAI_BITSINT >= 32 +typedef unsigned int Instruction; +#else +typedef unsigned long Instruction; +#endif -/* maximum stack for a Lua function */ -#define MAXSTACK 250 +/* +** Maximum length for short strings, that is, strings that are +** internalized. (Cannot be smaller than reserved words or tags for +** metamethods, as these strings must be internalized; +** #("function") = 8, #("__newindex") = 10.) +*/ +#if !defined(LUAI_MAXSHORTLEN) +#define LUAI_MAXSHORTLEN 40 +#endif -/* minimum size for the string table (must be power of 2) */ +/* +** Initial size for the string table (must be power of 2). +** The Lua core alone registers ~50 strings (reserved words + +** metaevent keys + a few others). Libraries would typically add +** a few dozens more. +*/ #if !defined(MINSTRTABSIZE) -#define MINSTRTABSIZE 32 +#define MINSTRTABSIZE 128 +#endif + + +/* +** Size of cache for strings in the API. 'N' is the number of +** sets (better be a prime) and "M" is the size of each set (M == 1 +** makes a direct cache.) +*/ +#if !defined(STRCACHE_N) +#define STRCACHE_N 53 +#define STRCACHE_M 2 #endif @@ -151,13 +206,21 @@ typedef lu_int32 Instruction; #endif +/* +** macros that are executed whenever program enters the Lua core +** ('lua_lock') and leaves the core ('lua_unlock') +*/ #if !defined(lua_lock) -#define lua_lock(L) ((void) 0) -#define lua_unlock(L) ((void) 0) +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) #endif +/* +** macro executed during Lua functions at points where the +** function can yield. +*/ #if !defined(luai_threadyield) -#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} #endif @@ -183,127 +246,78 @@ typedef lu_int32 Instruction; #endif #if !defined(luai_userstateresume) -#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateresume(L,n) ((void)L) #endif #if !defined(luai_userstateyield) -#define luai_userstateyield(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) #endif -/* -** lua_number2int is a macro to convert lua_Number to int. -** lua_number2integer is a macro to convert lua_Number to lua_Integer. -** lua_number2unsigned is a macro to convert a lua_Number to a lua_Unsigned. -** lua_unsigned2number is a macro to convert a lua_Unsigned to a lua_Number. -** luai_hashnum is a macro to hash a lua_Number value into an integer. -** The hash must be deterministic and give reasonable values for -** both small and large values (outside the range of integers). -*/ - -#if defined(MS_ASMTRICK) || defined(LUA_MSASMTRICK) /* { */ -/* trick with Microsoft assembler for X86 */ - -#define lua_number2int(i,n) __asm {__asm fld n __asm fistp i} -#define lua_number2integer(i,n) lua_number2int(i, n) -#define lua_number2unsigned(i,n) \ - {__int64 l; __asm {__asm fld n __asm fistp l} i = (unsigned int)l;} - - -#elif defined(LUA_IEEE754TRICK) /* }{ */ -/* the next trick should work on any machine using IEEE754 with - a 32-bit int type */ - -union luai_Cast { double l_d; LUA_INT32 l_p[2]; }; - -#if !defined(LUA_IEEEENDIAN) /* { */ -#define LUAI_EXTRAIEEE \ - static const union luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)}; -#define LUA_IEEEENDIANLOC (ieeeendian.l_p[1] == 33) -#else -#define LUA_IEEEENDIANLOC LUA_IEEEENDIAN -#define LUAI_EXTRAIEEE /* empty */ -#endif /* } */ -#define lua_number2int32(i,n,t) \ - { LUAI_EXTRAIEEE \ - volatile union luai_Cast u; u.l_d = (n) + 6755399441055744.0; \ - (i) = (t)u.l_p[LUA_IEEEENDIANLOC]; } -#define luai_hashnum(i,n) \ - { volatile union luai_Cast u; u.l_d = (n) + 1.0; /* avoid -0 */ \ - (i) = u.l_p[0]; (i) += u.l_p[1]; } /* add double bits for his hash */ - -#define lua_number2int(i,n) lua_number2int32(i, n, int) -#define lua_number2unsigned(i,n) lua_number2int32(i, n, lua_Unsigned) +/* +** The luai_num* macros define the primitive operations over numbers. +*/ -/* the trick can be expanded to lua_Integer when it is a 32-bit value */ -#if defined(LUA_IEEELL) -#define lua_number2integer(i,n) lua_number2int32(i, n, lua_Integer) +/* floor division (defined as 'floor(a/b)') */ +#if !defined(luai_numidiv) +#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) #endif -#endif /* } */ - - -/* the following definitions always work, but may be slow */ - -#if !defined(lua_number2int) -#define lua_number2int(i,n) ((i)=(int)(n)) +/* float division */ +#if !defined(luai_numdiv) +#define luai_numdiv(L,a,b) ((a)/(b)) #endif -#if !defined(lua_number2integer) -#define lua_number2integer(i,n) ((i)=(lua_Integer)(n)) +/* +** modulo: defined as 'a - floor(a/b)*b'; this definition gives NaN when +** 'b' is huge, but the result should be 'a'. 'fmod' gives the result of +** 'a - trunc(a/b)*b', and therefore must be corrected when 'trunc(a/b) +** ~= floor(a/b)'. That happens when the division has a non-integer +** negative result, which is equivalent to the test below. +*/ +#if !defined(luai_nummod) +#define luai_nummod(L,a,b,m) \ + { (m) = l_mathop(fmod)(a,b); if ((m)*(b) < 0) (m) += (b); } #endif -#if !defined(lua_number2unsigned) /* { */ -/* the following definition assures proper modulo behavior */ -#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_NUMBER_FLOAT) -#include -#define SUPUNSIGNED ((lua_Number)(~(lua_Unsigned)0) + 1) -#define lua_number2unsigned(i,n) \ - ((i)=(lua_Unsigned)((n) - floor((n)/SUPUNSIGNED)*SUPUNSIGNED)) -#else -#define lua_number2unsigned(i,n) ((i)=(lua_Unsigned)(n)) +/* exponentiation */ +#if !defined(luai_numpow) +#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b)) #endif -#endif /* } */ - -#if !defined(lua_unsigned2number) -/* on several machines, coercion from unsigned to double is slow, - so it may be worth to avoid */ -#define lua_unsigned2number(u) \ - (((u) <= (lua_Unsigned)INT_MAX) ? (lua_Number)(int)(u) : (lua_Number)(u)) +/* the others are quite standard operations */ +#if !defined(luai_numadd) +#define luai_numadd(L,a,b) ((a)+(b)) +#define luai_numsub(L,a,b) ((a)-(b)) +#define luai_nummul(L,a,b) ((a)*(b)) +#define luai_numunm(L,a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) #endif -#if defined(ltable_c) && !defined(luai_hashnum) - -#include -#include - -#define luai_hashnum(i,n) { int e; \ - n = l_mathop(frexp)(n, &e) * (lua_Number)(INT_MAX - DBL_MAX_EXP); \ - lua_number2int(i, n); i += e; } - -#endif - /* ** macro to control inclusion of some hard tests on stack reallocation */ #if !defined(HARDSTACKTESTS) -#define condmovestack(L) ((void)0) +#define condmovestack(L,pre,pos) ((void)0) #else /* realloc stack keeping its size */ -#define condmovestack(L) luaD_reallocstack((L), (L)->stacksize) +#define condmovestack(L,pre,pos) \ + { int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_); pos; } #endif #if !defined(HARDMEMTESTS) -#define condchangemem(L) condmovestack(L) +#define condchangemem(L,pre,pos) ((void)0) #else -#define condchangemem(L) \ - ((void)(!(G(L)->gcrunning) || (luaC_fullgc(L, 0), 1))) +#define condchangemem(L,pre,pos) \ + { if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } } #endif #endif diff --git a/deps/lua/lmathlib.c b/deps/lua/lmathlib.c index fe9fc54..94815f1 100644 --- a/deps/lua/lmathlib.c +++ b/deps/lua/lmathlib.c @@ -1,16 +1,18 @@ /* -** $Id: lmathlib.c,v 1.83.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lmathlib.c,v 1.117 2015/10/02 15:39:23 roberto Exp $ ** Standard mathematical library ** See Copyright Notice in lua.h */ +#define lmathlib_c +#define LUA_LIB + +#include "lprefix.h" + #include #include -#define lmathlib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -18,13 +20,30 @@ #undef PI -#define PI ((lua_Number)(3.1415926535897932384626433832795)) -#define RADIANS_PER_DEGREE ((lua_Number)(PI/180.0)) - +#define PI (l_mathop(3.141592653589793238462643383279502884)) + + +#if !defined(l_rand) /* { */ +#if defined(LUA_USE_POSIX) +#define l_rand() random() +#define l_srand(x) srandom(x) +#define L_RANDMAX 2147483647 /* (2^31 - 1), following POSIX */ +#else +#define l_rand() rand() +#define l_srand(x) srand(x) +#define L_RANDMAX RAND_MAX +#endif +#endif /* } */ static int math_abs (lua_State *L) { - lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); + if (lua_isinteger(L, 1)) { + lua_Integer n = lua_tointeger(L, 1); + if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); + lua_pushinteger(L, n); + } + else + lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); return 1; } @@ -33,31 +52,16 @@ static int math_sin (lua_State *L) { return 1; } -static int math_sinh (lua_State *L) { - lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); - return 1; -} - static int math_cos (lua_State *L) { lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); return 1; } -static int math_cosh (lua_State *L) { - lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); - return 1; -} - static int math_tan (lua_State *L) { lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); return 1; } -static int math_tanh (lua_State *L) { - lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); - return 1; -} - static int math_asin (lua_State *L) { lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); return 1; @@ -69,49 +73,106 @@ static int math_acos (lua_State *L) { } static int math_atan (lua_State *L) { - lua_pushnumber(L, l_mathop(atan)(luaL_checknumber(L, 1))); + lua_Number y = luaL_checknumber(L, 1); + lua_Number x = luaL_optnumber(L, 2, 1); + lua_pushnumber(L, l_mathop(atan2)(y, x)); return 1; } -static int math_atan2 (lua_State *L) { - lua_pushnumber(L, l_mathop(atan2)(luaL_checknumber(L, 1), - luaL_checknumber(L, 2))); + +static int math_toint (lua_State *L) { + int valid; + lua_Integer n = lua_tointegerx(L, 1, &valid); + if (valid) + lua_pushinteger(L, n); + else { + luaL_checkany(L, 1); + lua_pushnil(L); /* value is not convertible to integer */ + } return 1; } -static int math_ceil (lua_State *L) { - lua_pushnumber(L, l_mathop(ceil)(luaL_checknumber(L, 1))); - return 1; + +static void pushnumint (lua_State *L, lua_Number d) { + lua_Integer n; + if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */ + lua_pushinteger(L, n); /* result is integer */ + else + lua_pushnumber(L, d); /* result is float */ } + static int math_floor (lua_State *L) { - lua_pushnumber(L, l_mathop(floor)(luaL_checknumber(L, 1))); + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own floor */ + else { + lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + +static int math_ceil (lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own ceil */ + else { + lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } return 1; } + static int math_fmod (lua_State *L) { - lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), - luaL_checknumber(L, 2))); + if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) { + lua_Integer d = lua_tointeger(L, 2); + if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */ + luaL_argcheck(L, d != 0, 2, "zero"); + lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */ + } + else + lua_pushinteger(L, lua_tointeger(L, 1) % d); + } + else + lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), + luaL_checknumber(L, 2))); return 1; } + +/* +** next function does not use 'modf', avoiding problems with 'double*' +** (which is not compatible with 'float*') when lua_Number is not +** 'double'. +*/ static int math_modf (lua_State *L) { - lua_Number ip; - lua_Number fp = l_mathop(modf)(luaL_checknumber(L, 1), &ip); - lua_pushnumber(L, ip); - lua_pushnumber(L, fp); + if (lua_isinteger(L ,1)) { + lua_settop(L, 1); /* number is its own integer part */ + lua_pushnumber(L, 0); /* no fractional part */ + } + else { + lua_Number n = luaL_checknumber(L, 1); + /* integer part (rounds toward zero) */ + lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n); + pushnumint(L, ip); + /* fractional part (test needed for inf/-inf) */ + lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip)); + } return 2; } + static int math_sqrt (lua_State *L) { lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1))); return 1; } -static int math_pow (lua_State *L) { - lua_Number x = luaL_checknumber(L, 1); - lua_Number y = luaL_checknumber(L, 2); - lua_pushnumber(L, l_mathop(pow)(x, y)); + +static int math_ult (lua_State *L) { + lua_Integer a = luaL_checkinteger(L, 1); + lua_Integer b = luaL_checkinteger(L, 2); + lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b); return 1; } @@ -122,145 +183,208 @@ static int math_log (lua_State *L) { res = l_mathop(log)(x); else { lua_Number base = luaL_checknumber(L, 2); - if (base == (lua_Number)10.0) res = l_mathop(log10)(x); +#if !defined(LUA_USE_C89) + if (base == 2.0) res = l_mathop(log2)(x); else +#endif + if (base == 10.0) res = l_mathop(log10)(x); else res = l_mathop(log)(x)/l_mathop(log)(base); } lua_pushnumber(L, res); return 1; } -#if defined(LUA_COMPAT_LOG10) -static int math_log10 (lua_State *L) { - lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); - return 1; -} -#endif - static int math_exp (lua_State *L) { lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); return 1; } static int math_deg (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); return 1; } static int math_rad (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); - return 1; -} - -static int math_frexp (lua_State *L) { - int e; - lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); - lua_pushinteger(L, e); - return 2; -} - -static int math_ldexp (lua_State *L) { - lua_Number x = luaL_checknumber(L, 1); - int ep = luaL_checkint(L, 2); - lua_pushnumber(L, l_mathop(ldexp)(x, ep)); + lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); return 1; } - static int math_min (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ - lua_Number dmin = luaL_checknumber(L, 1); + int imin = 1; /* index of current minimum value */ int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d < dmin) - dmin = d; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, i, imin, LUA_OPLT)) + imin = i; } - lua_pushnumber(L, dmin); + lua_pushvalue(L, imin); return 1; } static int math_max (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ - lua_Number dmax = luaL_checknumber(L, 1); + int imax = 1; /* index of current maximum value */ int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d > dmax) - dmax = d; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, imax, i, LUA_OPLT)) + imax = i; } - lua_pushnumber(L, dmax); + lua_pushvalue(L, imax); return 1; } - +/* +** This function uses 'double' (instead of 'lua_Number') to ensure that +** all bits from 'l_rand' can be represented, and that 'RANDMAX + 1.0' +** will keep full precision (ensuring that 'r' is always less than 1.0.) +*/ static int math_random (lua_State *L) { - /* the `%' avoids the (rare) case of r==1, and is needed also because on - some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ - lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + lua_Integer low, up; + double r = (double)l_rand() * (1.0 / ((double)L_RANDMAX + 1.0)); switch (lua_gettop(L)) { /* check number of arguments */ case 0: { /* no arguments */ - lua_pushnumber(L, r); /* Number between 0 and 1 */ - break; + lua_pushnumber(L, (lua_Number)r); /* Number between 0 and 1 */ + return 1; } case 1: { /* only upper limit */ - lua_Number u = luaL_checknumber(L, 1); - luaL_argcheck(L, (lua_Number)1.0 <= u, 1, "interval is empty"); - lua_pushnumber(L, l_mathop(floor)(r*u) + (lua_Number)(1.0)); /* [1, u] */ + low = 1; + up = luaL_checkinteger(L, 1); break; } case 2: { /* lower and upper limits */ - lua_Number l = luaL_checknumber(L, 1); - lua_Number u = luaL_checknumber(L, 2); - luaL_argcheck(L, l <= u, 2, "interval is empty"); - lua_pushnumber(L, l_mathop(floor)(r*(u-l+1)) + l); /* [l, u] */ + low = luaL_checkinteger(L, 1); + up = luaL_checkinteger(L, 2); break; } default: return luaL_error(L, "wrong number of arguments"); } + /* random integer in the interval [low, up] */ + luaL_argcheck(L, low <= up, 1, "interval is empty"); + luaL_argcheck(L, low >= 0 || up <= LUA_MAXINTEGER + low, 1, + "interval too large"); + r *= (double)(up - low) + 1.0; + lua_pushinteger(L, (lua_Integer)r + low); return 1; } static int math_randomseed (lua_State *L) { - srand(luaL_checkunsigned(L, 1)); - (void)rand(); /* discard first value to avoid undesirable correlations */ + l_srand((unsigned int)(lua_Integer)luaL_checknumber(L, 1)); + (void)l_rand(); /* discard first value to avoid undesirable correlations */ return 0; } +static int math_type (lua_State *L) { + if (lua_type(L, 1) == LUA_TNUMBER) { + if (lua_isinteger(L, 1)) + lua_pushliteral(L, "integer"); + else + lua_pushliteral(L, "float"); + } + else { + luaL_checkany(L, 1); + lua_pushnil(L); + } + return 1; +} + + +/* +** {================================================================== +** Deprecated functions (for compatibility only) +** =================================================================== +*/ +#if defined(LUA_COMPAT_MATHLIB) + +static int math_cosh (lua_State *L) { + lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh (lua_State *L) { + lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh (lua_State *L) { + lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number y = luaL_checknumber(L, 2); + lua_pushnumber(L, l_mathop(pow)(x, y)); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); + lua_pushinteger(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep = (int)luaL_checkinteger(L, 2); + lua_pushnumber(L, l_mathop(ldexp)(x, ep)); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); + return 1; +} + +#endif +/* }================================================================== */ + + + static const luaL_Reg mathlib[] = { {"abs", math_abs}, {"acos", math_acos}, {"asin", math_asin}, - {"atan2", math_atan2}, {"atan", math_atan}, {"ceil", math_ceil}, - {"cosh", math_cosh}, {"cos", math_cos}, {"deg", math_deg}, {"exp", math_exp}, + {"tointeger", math_toint}, {"floor", math_floor}, {"fmod", math_fmod}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, -#if defined(LUA_COMPAT_LOG10) - {"log10", math_log10}, -#endif + {"ult", math_ult}, {"log", math_log}, {"max", math_max}, {"min", math_min}, {"modf", math_modf}, - {"pow", math_pow}, {"rad", math_rad}, {"random", math_random}, {"randomseed", math_randomseed}, - {"sinh", math_sinh}, {"sin", math_sin}, {"sqrt", math_sqrt}, - {"tanh", math_tanh}, {"tan", math_tan}, + {"type", math_type}, +#if defined(LUA_COMPAT_MATHLIB) + {"atan2", math_atan}, + {"cosh", math_cosh}, + {"sinh", math_sinh}, + {"tanh", math_tanh}, + {"pow", math_pow}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"log10", math_log10}, +#endif + /* placeholders */ + {"pi", NULL}, + {"huge", NULL}, + {"maxinteger", NULL}, + {"mininteger", NULL}, {NULL, NULL} }; @@ -272,8 +396,12 @@ LUAMOD_API int luaopen_math (lua_State *L) { luaL_newlib(L, mathlib); lua_pushnumber(L, PI); lua_setfield(L, -2, "pi"); - lua_pushnumber(L, HUGE_VAL); + lua_pushnumber(L, (lua_Number)HUGE_VAL); lua_setfield(L, -2, "huge"); + lua_pushinteger(L, LUA_MAXINTEGER); + lua_setfield(L, -2, "maxinteger"); + lua_pushinteger(L, LUA_MININTEGER); + lua_setfield(L, -2, "mininteger"); return 1; } diff --git a/deps/lua/lmem.c b/deps/lua/lmem.c index ee343e3..0a0476c 100644 --- a/deps/lua/lmem.c +++ b/deps/lua/lmem.c @@ -1,15 +1,17 @@ /* -** $Id: lmem.c,v 1.84.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lmem.c,v 1.91 2015/03/06 19:45:54 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ - -#include - #define lmem_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "ldebug.h" @@ -24,15 +26,15 @@ /* ** About the realloc function: ** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); -** (`osize' is the old size, `nsize' is the new size) +** ('osize' is the old size, 'nsize' is the new size) ** -** * frealloc(ud, NULL, x, s) creates a new block of size `s' (no +** * frealloc(ud, NULL, x, s) creates a new block of size 's' (no ** matter 'x'). ** -** * frealloc(ud, p, x, 0) frees the block `p' +** * frealloc(ud, p, x, 0) frees the block 'p' ** (in this specific case, frealloc must return NULL); ** particularly, frealloc(ud, NULL, 0, 0) does nothing -** (which is equivalent to free(NULL) in ANSI C) +** (which is equivalent to free(NULL) in ISO C) ** ** frealloc returns NULL if it cannot create or reallocate the area ** (any reallocation to an equal or smaller size cannot fail!) @@ -83,9 +85,8 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { #endif newblock = (*g->frealloc)(g->ud, block, osize, nsize); if (newblock == NULL && nsize > 0) { - api_check(L, nsize > realosize, - "realloc cannot fail when shrinking a block"); - if (g->gcrunning) { + lua_assert(nsize > realosize); /* cannot fail when shrinking a block */ + if (g->version) { /* is state fully built? */ luaC_fullgc(L, 1); /* try to free some memory... */ newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ } diff --git a/deps/lua/lmem.h b/deps/lua/lmem.h index bd4f4e0..30f4848 100644 --- a/deps/lua/lmem.h +++ b/deps/lua/lmem.h @@ -1,5 +1,5 @@ /* -** $Id: lmem.h,v 1.40.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lmem.h,v 1.43 2014/12/19 17:26:14 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ @@ -15,20 +15,32 @@ /* -** This macro avoids the runtime division MAX_SIZET/(e), as 'e' is -** always constant. -** The macro is somewhat complex to avoid warnings: -** +1 avoids warnings of "comparison has constant result"; -** cast to 'void' avoids warnings of "value unused". +** This macro reallocs a vector 'b' from 'on' to 'n' elements, where +** each element has size 'e'. In case of arithmetic overflow of the +** product 'n'*'e', it raises an error (calling 'luaM_toobig'). Because +** 'e' is always constant, it avoids the runtime division MAX_SIZET/(e). +** +** (The macro is somewhat complex to avoid warnings: The 'sizeof' +** comparison avoids a runtime comparison when overflow cannot occur. +** The compiler should be able to optimize the real test by itself, but +** when it does it, it may give a warning about "comparison is always +** false due to limited range of data type"; the +1 tricks the compiler, +** avoiding this warning but also this optimization.) */ #define luaM_reallocv(L,b,on,n,e) \ - (cast(void, \ - (cast(size_t, (n)+1) > MAX_SIZET/(e)) ? (luaM_toobig(L), 0) : 0), \ + (((sizeof(n) >= sizeof(size_t) && cast(size_t, (n)) + 1 > MAX_SIZET/(e)) \ + ? luaM_toobig(L) : cast_void(0)) , \ luaM_realloc_(L, (b), (on)*(e), (n)*(e))) +/* +** Arrays of chars do not need any test +*/ +#define luaM_reallocvchar(L,b,on,n) \ + cast(char *, luaM_realloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) + #define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) #define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) -#define luaM_freearray(L, b, n) luaM_reallocv(L, (b), n, 0, sizeof((b)[0])) +#define luaM_freearray(L, b, n) luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0) #define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s)) #define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) diff --git a/deps/lua/loadlib.c b/deps/lua/loadlib.c index bedbea3..2cdb220 100644 --- a/deps/lua/loadlib.c +++ b/deps/lua/loadlib.c @@ -1,5 +1,5 @@ /* -** $Id: loadlib.c,v 1.111.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: loadlib.c,v 1.127 2015/11/23 11:30:45 roberto Exp $ ** Dynamic library loader for Lua ** See Copyright Notice in lua.h ** @@ -8,22 +8,16 @@ ** systems. */ +#define loadlib_c +#define LUA_LIB -/* -** if needed, includes windows header before everything else -*/ -#if defined(_WIN32) -#include -#endif +#include "lprefix.h" +#include #include #include - -#define loadlib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -31,21 +25,21 @@ /* -** LUA_PATH and LUA_CPATH are the names of the environment +** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment ** variables that Lua check to set its paths. */ -#if !defined(LUA_PATH) -#define LUA_PATH "LUA_PATH" +#if !defined(LUA_PATH_VAR) +#define LUA_PATH_VAR "LUA_PATH" #endif -#if !defined(LUA_CPATH) -#define LUA_CPATH "LUA_CPATH" +#if !defined(LUA_CPATH_VAR) +#define LUA_CPATH_VAR "LUA_CPATH" #endif #define LUA_PATHSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR -#define LUA_PATHVERSION LUA_PATH LUA_PATHSUFFIX -#define LUA_CPATHVERSION LUA_CPATH LUA_PATHSUFFIX +#define LUA_PATHVARVERSION LUA_PATH_VAR LUA_PATHSUFFIX +#define LUA_CPATHVARVERSION LUA_CPATH_VAR LUA_PATHSUFFIX /* ** LUA_PATH_SEP is the character that separates templates in a path. @@ -92,29 +86,45 @@ #define LUA_OFSEP "_" -/* table (in the registry) that keeps handles for all loaded C libraries */ -#define CLIBS "_CLIBS" +/* +** unique key for table in the registry that keeps handles +** for all loaded C libraries +*/ +static const int CLIBS = 0; #define LIB_FAIL "open" - -/* error codes for ll_loadfunc */ -#define ERRLIB 1 -#define ERRFUNC 2 - #define setprogdir(L) ((void)0) /* ** system-dependent functions */ -static void ll_unloadlib (void *lib); -static void *ll_load (lua_State *L, const char *path, int seeglb); -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); +/* +** unload library 'lib' +*/ +static void lsys_unloadlib (void *lib); + +/* +** load C library in file 'path'. If 'seeglb', load with all names in +** the library global. +** Returns the library; in case of error, returns NULL plus an +** error string in the stack. +*/ +static void *lsys_load (lua_State *L, const char *path, int seeglb); + +/* +** Try to find a function named 'sym' in library 'lib'. +** Returns the function; in case of error, returns NULL plus an +** error string in the stack. +*/ +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym); -#if defined(LUA_USE_DLOPEN) + + +#if defined(LUA_USE_DLOPEN) /* { */ /* ** {======================================================================== ** This is an implementation of loadlib based on the dlfcn interface. @@ -126,20 +136,32 @@ static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); #include -static void ll_unloadlib (void *lib) { +/* +** Macro to convert pointer-to-void* to pointer-to-function. This cast +** is undefined according to ISO C, but POSIX assumes that it works. +** (The '__extension__' in gnu compilers is only to avoid warnings.) +*/ +#if defined(__GNUC__) +#define cast_func(p) (__extension__ (lua_CFunction)(p)) +#else +#define cast_func(p) ((lua_CFunction)(p)) +#endif + + +static void lsys_unloadlib (void *lib) { dlclose(lib); } -static void *ll_load (lua_State *L, const char *path, int seeglb) { +static void *lsys_load (lua_State *L, const char *path, int seeglb) { void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); if (lib == NULL) lua_pushstring(L, dlerror()); return lib; } -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { - lua_CFunction f = (lua_CFunction)dlsym(lib, sym); +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = cast_func(dlsym(lib, sym)); if (f == NULL) lua_pushstring(L, dlerror()); return f; } @@ -148,13 +170,15 @@ static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { -#elif defined(LUA_DL_DLL) +#elif defined(LUA_DL_DLL) /* }{ */ /* ** {====================================================================== ** This is an implementation of loadlib for Windows using native functions. ** ======================================================================= */ +#include + #undef setprogdir /* @@ -190,12 +214,12 @@ static void pusherror (lua_State *L) { lua_pushfstring(L, "system error %d\n", error); } -static void ll_unloadlib (void *lib) { +static void lsys_unloadlib (void *lib) { FreeLibrary((HMODULE)lib); } -static void *ll_load (lua_State *L, const char *path, int seeglb) { +static void *lsys_load (lua_State *L, const char *path, int seeglb) { HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS); (void)(seeglb); /* not used: symbols are 'global' by default */ if (lib == NULL) pusherror(L); @@ -203,7 +227,7 @@ static void *ll_load (lua_State *L, const char *path, int seeglb) { } -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = (lua_CFunction)GetProcAddress((HMODULE)lib, sym); if (f == NULL) pusherror(L); return f; @@ -212,7 +236,7 @@ static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { /* }====================================================== */ -#else +#else /* }{ */ /* ** {====================================================== ** Fallback for other systems @@ -226,31 +250,34 @@ static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { #define DLMSG "dynamic libraries not enabled; check your Lua installation" -static void ll_unloadlib (void *lib) { +static void lsys_unloadlib (void *lib) { (void)(lib); /* not used */ } -static void *ll_load (lua_State *L, const char *path, int seeglb) { +static void *lsys_load (lua_State *L, const char *path, int seeglb) { (void)(path); (void)(seeglb); /* not used */ lua_pushliteral(L, DLMSG); return NULL; } -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { (void)(lib); (void)(sym); /* not used */ lua_pushliteral(L, DLMSG); return NULL; } /* }====================================================== */ -#endif +#endif /* } */ -static void *ll_checkclib (lua_State *L, const char *path) { +/* +** return registry.CLIBS[path] +*/ +static void *checkclib (lua_State *L, const char *path) { void *plib; - lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS); lua_getfield(L, -1, path); plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */ lua_pop(L, 2); /* pop CLIBS table and 'plib' */ @@ -258,8 +285,12 @@ static void *ll_checkclib (lua_State *L, const char *path) { } -static void ll_addtoclib (lua_State *L, const char *path, void *plib) { - lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); +/* +** registry.CLIBS[path] = plib -- for queries +** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries +*/ +static void addtoclib (lua_State *L, const char *path, void *plib) { + lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS); lua_pushlightuserdata(L, plib); lua_pushvalue(L, -1); lua_setfield(L, -3, path); /* CLIBS[path] = plib */ @@ -267,35 +298,53 @@ static void ll_addtoclib (lua_State *L, const char *path, void *plib) { lua_pop(L, 1); /* pop CLIBS table */ } +//added by xdczju@sina.com +#define lsys_unloadlib(XX) /* -** __gc tag method for CLIBS table: calls 'll_unloadlib' for all lib +** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib ** handles in list CLIBS */ static int gctm (lua_State *L) { - int n = luaL_len(L, 1); + lua_Integer n = luaL_len(L, 1); for (; n >= 1; n--) { /* for each handle, in reverse order */ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ - ll_unloadlib(lua_touserdata(L, -1)); + lsys_unloadlib(lua_touserdata(L, -1)); lua_pop(L, 1); /* pop handle */ } return 0; } -static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { - void *reg = ll_checkclib(L, path); /* check loaded C libraries */ + +/* error codes for 'lookforfunc' */ +#define ERRLIB 1 +#define ERRFUNC 2 + +/* +** Look for a C function named 'sym' in a dynamically loaded library +** 'path'. +** First, check whether the library is already loaded; if not, try +** to load it. +** Then, if 'sym' is '*', return true (as library has been loaded). +** Otherwise, look for symbol 'sym' in the library and push a +** C function with that symbol. +** Return 0 and 'true' or a function in the stack; in case of +** errors, return an error code and an error message in the stack. +*/ +static int lookforfunc (lua_State *L, const char *path, const char *sym) { + void *reg = checkclib(L, path); /* check loaded C libraries */ if (reg == NULL) { /* must load library? */ - reg = ll_load(L, path, *sym == '*'); + reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */ if (reg == NULL) return ERRLIB; /* unable to load library */ - ll_addtoclib(L, path, reg); + addtoclib(L, path, reg); } if (*sym == '*') { /* loading only library (no function)? */ lua_pushboolean(L, 1); /* return 'true' */ return 0; /* no errors */ } else { - lua_CFunction f = ll_sym(L, reg, sym); + lua_CFunction f = lsys_sym(L, reg, sym); if (f == NULL) return ERRFUNC; /* unable to find function */ lua_pushcfunction(L, f); /* else create new function */ @@ -307,7 +356,7 @@ static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { static int ll_loadlib (lua_State *L) { const char *path = luaL_checkstring(L, 1); const char *init = luaL_checkstring(L, 2); - int stat = ll_loadfunc(L, path, init); + int stat = lookforfunc(L, path, init); if (stat == 0) /* no errors? */ return 1; /* return the loaded function */ else { /* error; error message is on stack top */ @@ -360,7 +409,7 @@ static const char *searchpath (lua_State *L, const char *name, lua_remove(L, -2); /* remove path template */ if (readable(filename)) /* does file exist and is readable? */ return filename; /* return that file name */ - lua_pushfstring(L, "\n\tno file " LUA_QS, filename); + lua_pushfstring(L, "\n\tno file '%s'", filename); lua_remove(L, -2); /* remove file name */ luaL_addvalue(&msg); /* concatenate error msg. entry */ } @@ -390,7 +439,7 @@ static const char *findfile (lua_State *L, const char *name, lua_getfield(L, lua_upvalueindex(1), pname); path = lua_tostring(L, -1); if (path == NULL) - luaL_error(L, LUA_QL("package.%s") " must be a string", pname); + luaL_error(L, "'package.%s' must be a string", pname); return searchpath(L, name, path, ".", dirsep); } @@ -401,8 +450,7 @@ static int checkload (lua_State *L, int stat, const char *filename) { return 2; /* return open function and file name */ } else - return luaL_error(L, "error loading module " LUA_QS - " from file " LUA_QS ":\n\t%s", + return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s", lua_tostring(L, 1), filename, lua_tostring(L, -1)); } @@ -416,21 +464,29 @@ static int searcher_Lua (lua_State *L) { } +/* +** Try to find a load function for module 'modname' at file 'filename'. +** First, change '.' to '_' in 'modname'; then, if 'modname' has +** the form X-Y (that is, it has an "ignore mark"), build a function +** name "luaopen_X" and look for it. (For compatibility, if that +** fails, it also tries "luaopen_Y".) If there is no ignore mark, +** look for a function named "luaopen_modname". +*/ static int loadfunc (lua_State *L, const char *filename, const char *modname) { - const char *funcname; + const char *openfunc; const char *mark; modname = luaL_gsub(L, modname, ".", LUA_OFSEP); mark = strchr(modname, *LUA_IGMARK); if (mark) { int stat; - funcname = lua_pushlstring(L, modname, mark - modname); - funcname = lua_pushfstring(L, LUA_POF"%s", funcname); - stat = ll_loadfunc(L, filename, funcname); + openfunc = lua_pushlstring(L, modname, mark - modname); + openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); + stat = lookforfunc(L, filename, openfunc); if (stat != ERRFUNC) return stat; modname = mark + 1; /* else go ahead and try old-style name */ } - funcname = lua_pushfstring(L, LUA_POF"%s", modname); - return ll_loadfunc(L, filename, funcname); + openfunc = lua_pushfstring(L, LUA_POF"%s", modname); + return lookforfunc(L, filename, openfunc); } @@ -455,8 +511,7 @@ static int searcher_Croot (lua_State *L) { if (stat != ERRFUNC) return checkload(L, 0, filename); /* real error */ else { /* open function not found */ - lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, - name, filename); + lua_pushfstring(L, "\n\tno module '%s' in file '%s'", name, filename); return 1; } } @@ -468,8 +523,7 @@ static int searcher_Croot (lua_State *L) { static int searcher_preload (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_getfield(L, LUA_REGISTRYINDEX, "_PRELOAD"); - lua_getfield(L, -1, name); - if (lua_isnil(L, -1)) /* not found? */ + if (lua_getfield(L, -1, name) == LUA_TNIL) /* not found? */ lua_pushfstring(L, "\n\tno field package.preload['%s']", name); return 1; } @@ -479,17 +533,15 @@ static void findloader (lua_State *L, const char *name) { int i; luaL_Buffer msg; /* to build error message */ luaL_buffinit(L, &msg); - lua_getfield(L, lua_upvalueindex(1), "searchers"); /* will be at index 3 */ - if (!lua_istable(L, 3)) - luaL_error(L, LUA_QL("package.searchers") " must be a table"); + /* push 'package.searchers' to index 3 in the stack */ + if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE) + luaL_error(L, "'package.searchers' must be a table"); /* iterate over available searchers to find a loader */ for (i = 1; ; i++) { - lua_rawgeti(L, 3, i); /* get a searcher */ - if (lua_isnil(L, -1)) { /* no more searchers? */ + if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */ lua_pop(L, 1); /* remove nil */ luaL_pushresult(&msg); /* create error message */ - luaL_error(L, "module " LUA_QS " not found:%s", - name, lua_tostring(L, -1)); + luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); } lua_pushstring(L, name); lua_call(L, 1, 2); /* call it */ @@ -520,8 +572,7 @@ static int ll_require (lua_State *L) { lua_call(L, 2, 1); /* run loader to load module */ if (!lua_isnil(L, -1)) /* non-nil return? */ lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ - lua_getfield(L, 2, name); - if (lua_isnil(L, -1)) { /* module did not set a value? */ + if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ lua_pushboolean(L, 1); /* use true as result */ lua_pushvalue(L, -1); /* extra copy to be returned */ lua_setfield(L, 2, name); /* _LOADED[name] = true */ @@ -548,7 +599,7 @@ static void set_env (lua_State *L) { if (lua_getstack(L, 1, &ar) == 0 || lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ lua_iscfunction(L, -1)) - luaL_error(L, LUA_QL("module") " not called from a Lua function"); + luaL_error(L, "'module' not called from a Lua function"); lua_pushvalue(L, -2); /* copy new environment table to top */ lua_setupvalue(L, -2, 1); lua_pop(L, 1); /* remove function */ @@ -587,9 +638,8 @@ static int ll_module (lua_State *L) { int lastarg = lua_gettop(L); /* last parameter */ luaL_pushmodule(L, modname, 1); /* get/create module table */ /* check whether table already has a _NAME field */ - lua_getfield(L, -1, "_NAME"); - if (!lua_isnil(L, -1)) /* is table an initialized module? */ - lua_pop(L, 1); + if (lua_getfield(L, -1, "_NAME") != LUA_TNIL) + lua_pop(L, 1); /* table is an initialized module */ else { /* no; initialize it */ lua_pop(L, 1); modinit(L, modname); @@ -659,6 +709,12 @@ static const luaL_Reg pk_funcs[] = { #if defined(LUA_COMPAT_MODULE) {"seeall", ll_seeall}, #endif + /* placeholders */ + {"preload", NULL}, + {"cpath", NULL}, + {"path", NULL}, + {"searchers", NULL}, + {"loaded", NULL}, {NULL, NULL} }; @@ -678,42 +734,50 @@ static void createsearcherstable (lua_State *L) { int i; /* create 'searchers' table */ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); - /* fill it with pre-defined searchers */ + /* fill it with predefined searchers */ for (i=0; searchers[i] != NULL; i++) { lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ lua_pushcclosure(L, searchers[i], 1); lua_rawseti(L, -2, i+1); } +#if defined(LUA_COMPAT_LOADERS) + lua_pushvalue(L, -1); /* make a copy of 'searchers' table */ + lua_setfield(L, -3, "loaders"); /* put it in field 'loaders' */ +#endif + lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ } -LUAMOD_API int luaopen_package (lua_State *L) { - /* create table CLIBS to keep track of loaded C libraries */ - luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); - lua_createtable(L, 0, 1); /* metatable for CLIBS */ +/* +** create table CLIBS to keep track of loaded C libraries, +** setting a finalizer to close all libraries when closing state. +*/ +static void createclibstable (lua_State *L) { + lua_newtable(L); /* create CLIBS table */ + lua_createtable(L, 0, 1); /* create metatable for CLIBS */ lua_pushcfunction(L, gctm); lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ lua_setmetatable(L, -2); - /* create `package' table */ - luaL_newlib(L, pk_funcs); + lua_rawsetp(L, LUA_REGISTRYINDEX, &CLIBS); /* set CLIBS table in registry */ +} + + +LUAMOD_API int luaopen_package (lua_State *L) { + createclibstable(L); + luaL_newlib(L, pk_funcs); /* create 'package' table */ createsearcherstable(L); -#if defined(LUA_COMPAT_LOADERS) - lua_pushvalue(L, -1); /* make a copy of 'searchers' table */ - lua_setfield(L, -3, "loaders"); /* put it in field `loaders' */ -#endif - lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ /* set field 'path' */ - setpath(L, "path", LUA_PATHVERSION, LUA_PATH, LUA_PATH_DEFAULT); + setpath(L, "path", LUA_PATHVARVERSION, LUA_PATH_VAR, LUA_PATH_DEFAULT); /* set field 'cpath' */ - setpath(L, "cpath", LUA_CPATHVERSION, LUA_CPATH, LUA_CPATH_DEFAULT); + setpath(L, "cpath", LUA_CPATHVARVERSION, LUA_CPATH_VAR, LUA_CPATH_DEFAULT); /* store config information */ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n" LUA_EXEC_DIR "\n" LUA_IGMARK "\n"); lua_setfield(L, -2, "config"); - /* set field `loaded' */ + /* set field 'loaded' */ luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); lua_setfield(L, -2, "loaded"); - /* set field `preload' */ + /* set field 'preload' */ luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD"); lua_setfield(L, -2, "preload"); lua_pushglobaltable(L); diff --git a/deps/lua/lobject.c b/deps/lua/lobject.c index 882d994..e24723f 100644 --- a/deps/lua/lobject.c +++ b/deps/lua/lobject.c @@ -1,17 +1,22 @@ /* -** $Id: lobject.c,v 2.58.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lobject.c,v 2.108 2015/11/02 16:09:30 roberto Exp $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ +#define lobject_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include #include #include #include #include -#define lobject_c -#define LUA_CORE - #include "lua.h" #include "lctype.h" @@ -36,8 +41,12 @@ LUAI_DDEF const TValue luaO_nilobject_ = {NILCONSTANT}; int luaO_int2fb (unsigned int x) { int e = 0; /* exponent */ if (x < 8) return x; - while (x >= 0x10) { - x = (x+1) >> 1; + while (x >= (8 << 4)) { /* coarse steps */ + x = (x + 0xf) >> 4; /* x = ceil(x / 16) */ + e += 4; + } + while (x >= (8 << 1)) { /* fine steps */ + x = (x + 1) >> 1; /* x = ceil(x / 2) */ e++; } return ((e+1) << 3) | (cast_int(x) - 8); @@ -46,14 +55,15 @@ int luaO_int2fb (unsigned int x) { /* converts back */ int luaO_fb2int (int x) { - int e = (x >> 3) & 0x1f; - if (e == 0) return x; - else return ((x & 7) + 8) << (e - 1); + return (x < 8) ? x : ((x & 7) + 8) << ((x >> 3) - 1); } +/* +** Computes ceil(log2(x)) +*/ int luaO_ceillog2 (unsigned int x) { - static const lu_byte log_2[256] = { + static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,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, 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, @@ -70,29 +80,90 @@ int luaO_ceillog2 (unsigned int x) { } -lua_Number luaO_arith (int op, lua_Number v1, lua_Number v2) { +static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, + lua_Integer v2) { switch (op) { - case LUA_OPADD: return luai_numadd(NULL, v1, v2); - case LUA_OPSUB: return luai_numsub(NULL, v1, v2); - case LUA_OPMUL: return luai_nummul(NULL, v1, v2); - case LUA_OPDIV: return luai_numdiv(NULL, v1, v2); - case LUA_OPMOD: return luai_nummod(NULL, v1, v2); - case LUA_OPPOW: return luai_numpow(NULL, v1, v2); - case LUA_OPUNM: return luai_numunm(NULL, v1); + case LUA_OPADD: return intop(+, v1, v2); + case LUA_OPSUB:return intop(-, v1, v2); + case LUA_OPMUL:return intop(*, v1, v2); + case LUA_OPMOD: return luaV_mod(L, v1, v2); + case LUA_OPIDIV: return luaV_div(L, v1, v2); + case LUA_OPBAND: return intop(&, v1, v2); + case LUA_OPBOR: return intop(|, v1, v2); + case LUA_OPBXOR: return intop(^, v1, v2); + case LUA_OPSHL: return luaV_shiftl(v1, v2); + case LUA_OPSHR: return luaV_shiftl(v1, -v2); + case LUA_OPUNM: return intop(-, 0, v1); + case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; } } -int luaO_hexavalue (int c) { - if (lisdigit(c)) return c - '0'; - else return ltolower(c) - 'a' + 10; +static lua_Number numarith (lua_State *L, int op, lua_Number v1, + lua_Number v2) { + switch (op) { + case LUA_OPADD: return luai_numadd(L, v1, v2); + case LUA_OPSUB: return luai_numsub(L, v1, v2); + case LUA_OPMUL: return luai_nummul(L, v1, v2); + case LUA_OPDIV: return luai_numdiv(L, v1, v2); + case LUA_OPPOW: return luai_numpow(L, v1, v2); + case LUA_OPIDIV: return luai_numidiv(L, v1, v2); + case LUA_OPUNM: return luai_numunm(L, v1); + case LUA_OPMOD: { + lua_Number m; + luai_nummod(L, v1, v2, m); + return m; + } + default: lua_assert(0); return 0; + } } -#if !defined(lua_strx2number) +void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, + TValue *res) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: + case LUA_OPBNOT: { /* operate only on integers */ + lua_Integer i1; lua_Integer i2; + if (tointeger(p1, &i1) && tointeger(p2, &i2)) { + setivalue(res, intarith(L, op, i1, i2)); + return; + } + else break; /* go to the end */ + } + case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */ + lua_Number n1; lua_Number n2; + if (tonumber(p1, &n1) && tonumber(p2, &n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return; + } + else break; /* go to the end */ + } + default: { /* other operations */ + lua_Number n1; lua_Number n2; + if (ttisinteger(p1) && ttisinteger(p2)) { + setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); + return; + } + else if (tonumber(p1, &n1) && tonumber(p2, &n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return; + } + else break; /* go to the end */ + } + } + /* could not perform raw operation; try metamethod */ + lua_assert(L != NULL); /* should not fail when folding (compile time) */ + luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); +} -#include + +int luaO_hexavalue (int c) { + if (lisdigit(c)) return c - '0'; + else return (ltolower(c) - 'a') + 10; +} static int isneg (const char **s) { @@ -102,86 +173,191 @@ static int isneg (const char **s) { } -static lua_Number readhexa (const char **s, lua_Number r, int *count) { - for (; lisxdigit(cast_uchar(**s)); (*s)++) { /* read integer part */ - r = (r * cast_num(16.0)) + cast_num(luaO_hexavalue(cast_uchar(**s))); - (*count)++; - } - return r; -} +/* +** {================================================================== +** Lua's implementation for 'lua_strx2number' +** =================================================================== +*/ + +#if !defined(lua_strx2number) + +/* maximum number of significant digits to read (to avoid overflows + even with single floats) */ +#define MAXSIGDIG 30 /* ** convert an hexadecimal numeric string to a number, following ** C99 specification for 'strtod' */ static lua_Number lua_strx2number (const char *s, char **endptr) { - lua_Number r = 0.0; - int e = 0, i = 0; - int neg = 0; /* 1 if number is negative */ + int dot = lua_getlocaledecpoint(); + lua_Number r = 0.0; /* result (accumulator) */ + int sigdig = 0; /* number of significant digits */ + int nosigdig = 0; /* number of non-significant digits */ + int e = 0; /* exponent correction */ + int neg; /* 1 if number is negative */ + int hasdot = 0; /* true after seen a dot */ *endptr = cast(char *, s); /* nothing is valid yet */ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); /* check signal */ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ return 0.0; /* invalid format (no '0x') */ - s += 2; /* skip '0x' */ - r = readhexa(&s, r, &i); /* read integer part */ - if (*s == '.') { - s++; /* skip dot */ - r = readhexa(&s, r, &e); /* read fractional part */ + for (s += 2; ; s++) { /* skip '0x' and read numeral */ + if (*s == dot) { + if (hasdot) break; /* second dot? stop loop */ + else hasdot = 1; + } + else if (lisxdigit(cast_uchar(*s))) { + if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ + nosigdig++; + else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ + r = (r * cast_num(16.0)) + luaO_hexavalue(*s); + else e++; /* too many digits; ignore, but still count for exponent */ + if (hasdot) e--; /* decimal digit? correct exponent */ + } + else break; /* neither a dot nor a digit */ } - if (i == 0 && e == 0) - return 0.0; /* invalid format (no digit) */ - e *= -4; /* each fractional digit divides value by 2^-4 */ + if (nosigdig + sigdig == 0) /* no digits? */ + return 0.0; /* invalid format */ *endptr = cast(char *, s); /* valid up to here */ + e *= 4; /* each digit multiplies/divides value by 2^4 */ if (*s == 'p' || *s == 'P') { /* exponent part? */ - int exp1 = 0; - int neg1; + int exp1 = 0; /* exponent value */ + int neg1; /* exponent signal */ s++; /* skip 'p' */ neg1 = isneg(&s); /* signal */ if (!lisdigit(cast_uchar(*s))) - goto ret; /* must have at least one digit */ + return 0.0; /* invalid; must have at least one digit */ while (lisdigit(cast_uchar(*s))) /* read exponent */ exp1 = exp1 * 10 + *(s++) - '0'; if (neg1) exp1 = -exp1; e += exp1; + *endptr = cast(char *, s); /* valid up to here */ } - *endptr = cast(char *, s); /* valid up to here */ - ret: if (neg) r = -r; return l_mathop(ldexp)(r, e); } #endif +/* }====================================================== */ -int luaO_str2d (const char *s, size_t len, lua_Number *result) { +static const char *l_str2d (const char *s, lua_Number *result) { char *endptr; if (strpbrk(s, "nN")) /* reject 'inf' and 'nan' */ - return 0; - else if (strpbrk(s, "xX")) /* hexa? */ + return NULL; + else if (strpbrk(s, "xX")) /* hex? */ *result = lua_strx2number(s, &endptr); else *result = lua_str2number(s, &endptr); - if (endptr == s) return 0; /* nothing recognized */ + if (endptr == s) return NULL; /* nothing recognized */ while (lisspace(cast_uchar(*endptr))) endptr++; - return (endptr == s + len); /* OK if no trailing characters */ + return (*endptr == '\0' ? endptr : NULL); /* OK if no trailing characters */ +} + + +static const char *l_str2int (const char *s, lua_Integer *result) { + lua_Unsigned a = 0; + int empty = 1; + int neg; + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); + if (s[0] == '0' && + (s[1] == 'x' || s[1] == 'X')) { /* hex? */ + s += 2; /* skip '0x' */ + for (; lisxdigit(cast_uchar(*s)); s++) { + a = a * 16 + luaO_hexavalue(*s); + empty = 0; + } + } + else { /* decimal */ + for (; lisdigit(cast_uchar(*s)); s++) { + a = a * 10 + *s - '0'; + empty = 0; + } + } + while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ + if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ + else { + *result = l_castU2S((neg) ? 0u - a : a); + return s; + } +} + + +size_t luaO_str2num (const char *s, TValue *o) { + lua_Integer i; lua_Number n; + const char *e; + if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ + setivalue(o, i); + } + else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ + setfltvalue(o, n); + } + else + return 0; /* conversion failed */ + return (e - s) + 1; /* success; return string size */ +} + + +int luaO_utf8esc (char *buff, unsigned long x) { + int n = 1; /* number of bytes put in buffer (backwards) */ + lua_assert(x <= 0x10FFFF); + if (x < 0x80) /* ascii? */ + buff[UTF8BUFFSZ - 1] = cast(char, x); + else { /* need continuation bytes */ + unsigned int mfb = 0x3f; /* maximum that fits in first byte */ + do { /* add continuation bytes */ + buff[UTF8BUFFSZ - (n++)] = cast(char, 0x80 | (x & 0x3f)); + x >>= 6; /* remove added bits */ + mfb >>= 1; /* now there is one less bit available in first byte */ + } while (x > mfb); /* still needs continuation byte? */ + buff[UTF8BUFFSZ - n] = cast(char, (~mfb << 1) | x); /* add first byte */ + } + return n; } +/* maximum length of the conversion of a number to a string */ +#define MAXNUMBER2STR 50 + + +/* +** Convert a number object to a string +*/ +void luaO_tostring (lua_State *L, StkId obj) { + char buff[MAXNUMBER2STR]; + size_t len; + lua_assert(ttisnumber(obj)); + if (ttisinteger(obj)) + len = lua_integer2str(buff, sizeof(buff), ivalue(obj)); + else { + len = lua_number2str(buff, sizeof(buff), fltvalue(obj)); +#if !defined(LUA_COMPAT_FLOATSTRING) + if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ + buff[len++] = lua_getlocaledecpoint(); + buff[len++] = '0'; /* adds '.0' to result */ + } +#endif + } + setsvalue2s(L, obj, luaS_newlstr(L, buff, len)); +} + static void pushstr (lua_State *L, const char *str, size_t l) { - setsvalue2s(L, L->top++, luaS_newlstr(L, str, l)); + setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); + luaD_inctop(L); } -/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ +/* this function handles only '%d', '%c', '%f', '%p', and '%s' + conventional formats, plus Lua-specific '%I' and '%U' */ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { int n = 0; for (;;) { const char *e = strchr(fmt, '%'); if (e == NULL) break; - luaD_checkstack(L, 2); /* fmt + item */ pushstr(L, fmt, e - fmt); switch (*(e+1)) { case 's': { @@ -191,33 +367,47 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { break; } case 'c': { - char buff; - buff = cast(char, va_arg(argp, int)); - pushstr(L, &buff, 1); + char buff = cast(char, va_arg(argp, int)); + if (lisprint(cast_uchar(buff))) + pushstr(L, &buff, 1); + else /* non-printable character; print its code */ + luaO_pushfstring(L, "<\\%d>", cast_uchar(buff)); break; } case 'd': { - setnvalue(L->top++, cast_num(va_arg(argp, int))); - break; + setivalue(L->top, va_arg(argp, int)); + goto top2str; + } + case 'I': { + setivalue(L->top, cast(lua_Integer, va_arg(argp, l_uacInt))); + goto top2str; } case 'f': { - setnvalue(L->top++, cast_num(va_arg(argp, l_uacNumber))); + setfltvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); + top2str: + luaD_inctop(L); + luaO_tostring(L, L->top - 1); break; } case 'p': { - char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ - int l = sprintf(buff, "%p", va_arg(argp, void *)); + char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */ + int l = l_sprintf(buff, sizeof(buff), "%p", va_arg(argp, void *)); pushstr(L, buff, l); break; } + case 'U': { + char buff[UTF8BUFFSZ]; + int l = luaO_utf8esc(buff, cast(long, va_arg(argp, long))); + pushstr(L, buff + UTF8BUFFSZ - l, l); + break; + } case '%': { pushstr(L, "%", 1); break; } default: { - luaG_runerror(L, - "invalid option " LUA_QL("%%%c") " to " LUA_QL("lua_pushfstring"), - *(e + 1)); + luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", + *(e + 1)); } } n += 2; diff --git a/deps/lua/lobject.h b/deps/lua/lobject.h index 4b8fe6c..6f5ecf8 100644 --- a/deps/lua/lobject.h +++ b/deps/lua/lobject.h @@ -1,5 +1,5 @@ /* -** $Id: lobject.h,v 2.71.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lobject.h,v 2.116 2015/11/03 18:33:10 roberto Exp $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ @@ -19,14 +19,13 @@ /* ** Extra tags for non-values */ -#define LUA_TPROTO LUA_NUMTAGS -#define LUA_TUPVAL (LUA_NUMTAGS+1) -#define LUA_TDEADKEY (LUA_NUMTAGS+2) +#define LUA_TPROTO LUA_NUMTAGS /* function prototypes */ +#define LUA_TDEADKEY (LUA_NUMTAGS+1) /* removed keys in tables */ /* ** number of all possible tags (including LUA_TNONE but excluding DEADKEY) */ -#define LUA_TOTALTAGS (LUA_TUPVAL+2) +#define LUA_TOTALTAGS (LUA_TPROTO + 2) /* @@ -36,8 +35,6 @@ ** bit 6: whether value is collectable */ -#define VARBITS (3 << 4) - /* ** LUA_TFUNCTION variants: @@ -57,6 +54,11 @@ #define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) /* long strings */ +/* Variant tags for numbers */ +#define LUA_TNUMFLT (LUA_TNUMBER | (0 << 4)) /* float numbers */ +#define LUA_TNUMINT (LUA_TNUMBER | (1 << 4)) /* integer numbers */ + + /* Bit mark for collectable types */ #define BIT_ISCOLLECTABLE (1 << 6) @@ -65,9 +67,9 @@ /* -** Union of all collectable objects +** Common type for all collectable objects */ -typedef union GCObject GCObject; +typedef struct GCObject GCObject; /* @@ -78,21 +80,12 @@ typedef union GCObject GCObject; /* -** Common header in struct form +** Common type has only the common header */ -typedef struct GCheader { +struct GCObject { CommonHeader; -} GCheader; - - - -/* -** Union of all Lua values -*/ -typedef union Value Value; - +}; -#define numfield lua_Number n; /* numbers */ @@ -101,9 +94,26 @@ typedef union Value Value; ** an actual value plus a tag with its type. */ +/* +** Union of all Lua values +*/ +typedef union Value { + GCObject *gc; /* collectable objects */ + void *p; /* light userdata */ + int b; /* booleans */ + lua_CFunction f; /* light C functions */ + lua_Integer i; /* integer numbers */ + lua_Number n; /* float numbers */ +} Value; + + #define TValuefields Value value_; int tt_ -typedef struct lua_TValue TValue; + +typedef struct lua_TValue { + TValuefields; +} TValue; + /* macro defining a nil value */ @@ -111,7 +121,6 @@ typedef struct lua_TValue TValue; #define val_(o) ((o)->value_) -#define num_(o) (val_(o).n) /* raw type tag of a TValue */ @@ -124,13 +133,15 @@ typedef struct lua_TValue TValue; #define ttype(o) (rttype(o) & 0x3F) /* type tag of a TValue with no variants (bits 0-3) */ -#define ttypenv(o) (novariant(rttype(o))) +#define ttnov(o) (novariant(rttype(o))) /* Macros to test type */ #define checktag(o,t) (rttype(o) == (t)) -#define checktype(o,t) (ttypenv(o) == (t)) -#define ttisnumber(o) checktag((o), LUA_TNUMBER) +#define checktype(o,t) (ttnov(o) == (t)) +#define ttisnumber(o) checktype((o), LUA_TNUMBER) +#define ttisfloat(o) checktag((o), LUA_TNUMFLT) +#define ttisinteger(o) checktag((o), LUA_TNUMINT) #define ttisnil(o) checktag((o), LUA_TNIL) #define ttisboolean(o) checktag((o), LUA_TBOOLEAN) #define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA) @@ -143,27 +154,27 @@ typedef struct lua_TValue TValue; #define ttisCclosure(o) checktag((o), ctb(LUA_TCCL)) #define ttisLclosure(o) checktag((o), ctb(LUA_TLCL)) #define ttislcf(o) checktag((o), LUA_TLCF) -#define ttisuserdata(o) checktag((o), ctb(LUA_TUSERDATA)) +#define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA)) #define ttisthread(o) checktag((o), ctb(LUA_TTHREAD)) #define ttisdeadkey(o) checktag((o), LUA_TDEADKEY) -#define ttisequal(o1,o2) (rttype(o1) == rttype(o2)) /* Macros to access values */ -#define nvalue(o) check_exp(ttisnumber(o), num_(o)) +#define ivalue(o) check_exp(ttisinteger(o), val_(o).i) +#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n) +#define nvalue(o) check_exp(ttisnumber(o), \ + (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o))) #define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) #define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) -#define rawtsvalue(o) check_exp(ttisstring(o), &val_(o).gc->ts) -#define tsvalue(o) (&rawtsvalue(o)->tsv) -#define rawuvalue(o) check_exp(ttisuserdata(o), &val_(o).gc->u) -#define uvalue(o) (&rawuvalue(o)->uv) -#define clvalue(o) check_exp(ttisclosure(o), &val_(o).gc->cl) -#define clLvalue(o) check_exp(ttisLclosure(o), &val_(o).gc->cl.l) -#define clCvalue(o) check_exp(ttisCclosure(o), &val_(o).gc->cl.c) +#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc)) +#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc)) +#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc)) +#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc)) +#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc)) #define fvalue(o) check_exp(ttislcf(o), val_(o).f) -#define hvalue(o) check_exp(ttistable(o), &val_(o).gc->h) +#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) #define bvalue(o) check_exp(ttisboolean(o), val_(o).b) -#define thvalue(o) check_exp(ttisthread(o), &val_(o).gc->th) +#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) /* a dead value may get the 'gc' field, but cannot access its contents */ #define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc)) @@ -174,18 +185,27 @@ typedef struct lua_TValue TValue; /* Macros for internal tests */ -#define righttt(obj) (ttype(obj) == gcvalue(obj)->gch.tt) +#define righttt(obj) (ttype(obj) == gcvalue(obj)->tt) -#define checkliveness(g,obj) \ +#define checkliveness(L,obj) \ lua_longassert(!iscollectable(obj) || \ - (righttt(obj) && !isdead(g,gcvalue(obj)))) + (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))) /* Macros to set values */ #define settt_(o,t) ((o)->tt_=(t)) -#define setnvalue(obj,x) \ - { TValue *io=(obj); num_(io)=(x); settt_(io, LUA_TNUMBER); } +#define setfltvalue(obj,x) \ + { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); } + +#define chgfltvalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); } + +#define setivalue(obj,x) \ + { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); } + +#define chgivalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); } #define setnilvalue(obj) settt_(obj, LUA_TNIL) @@ -199,48 +219,46 @@ typedef struct lua_TValue TValue; { TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); } #define setgcovalue(L,obj,x) \ - { TValue *io=(obj); GCObject *i_g=(x); \ - val_(io).gc=i_g; settt_(io, ctb(gch(i_g)->tt)); } + { TValue *io = (obj); GCObject *i_g=(x); \ + val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); } #define setsvalue(L,obj,x) \ - { TValue *io=(obj); \ - TString *x_ = (x); \ - val_(io).gc=cast(GCObject *, x_); settt_(io, ctb(x_->tsv.tt)); \ - checkliveness(G(L),io); } + { TValue *io = (obj); TString *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ + checkliveness(L,io); } #define setuvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TUSERDATA)); \ - checkliveness(G(L),io); } + { TValue *io = (obj); Udata *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \ + checkliveness(L,io); } #define setthvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTHREAD)); \ - checkliveness(G(L),io); } + { TValue *io = (obj); lua_State *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \ + checkliveness(L,io); } #define setclLvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TLCL)); \ - checkliveness(G(L),io); } + { TValue *io = (obj); LClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \ + checkliveness(L,io); } #define setclCvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TCCL)); \ - checkliveness(G(L),io); } + { TValue *io = (obj); CClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \ + checkliveness(L,io); } #define sethvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTABLE)); \ - checkliveness(G(L),io); } + { TValue *io = (obj); Table *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \ + checkliveness(L,io); } #define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY) #define setobj(L,obj1,obj2) \ - { const TValue *io2=(obj2); TValue *io1=(obj1); \ - io1->value_ = io2->value_; io1->tt_ = io2->tt_; \ - checkliveness(G(L),io1); } + { TValue *io1=(obj1); *io1 = *(obj2); \ + (void)L; checkliveness(L,io1); } /* @@ -256,196 +274,118 @@ typedef struct lua_TValue TValue; #define setptvalue2s setptvalue /* from table to same table */ #define setobjt2t setobj -/* to table */ -#define setobj2t setobj /* to new object */ #define setobj2n setobj #define setsvalue2n setsvalue +/* to table (define it as an expression to be used in macros) */ +#define setobj2t(L,o1,o2) ((void)L, *(o1)=*(o2), checkliveness(L,(o1))) + -/* check whether a number is valid (useful only for NaN trick) */ -#define luai_checknum(L,o,c) { /* empty */ } /* ** {====================================================== -** NaN Trick +** types and prototypes ** ======================================================= */ -#if defined(LUA_NANTRICK) - -/* -** numbers are represented in the 'd_' field. All other values have the -** value (NNMARK | tag) in 'tt__'. A number with such pattern would be -** a "signaled NaN", which is never generated by regular operations by -** the CPU (nor by 'strtod') -*/ - -/* allows for external implementation for part of the trick */ -#if !defined(NNMARK) /* { */ - - -#if !defined(LUA_IEEEENDIAN) -#error option 'LUA_NANTRICK' needs 'LUA_IEEEENDIAN' -#endif - - -#define NNMARK 0x7FF7A500 -#define NNMASK 0x7FFFFF00 - -#undef TValuefields -#undef NILCONSTANT - -#if (LUA_IEEEENDIAN == 0) /* { */ - -/* little endian */ -#define TValuefields \ - union { struct { Value v__; int tt__; } i; double d__; } u -#define NILCONSTANT {{{NULL}, tag2tt(LUA_TNIL)}} -/* field-access macros */ -#define v_(o) ((o)->u.i.v__) -#define d_(o) ((o)->u.d__) -#define tt_(o) ((o)->u.i.tt__) -#else /* }{ */ -/* big endian */ -#define TValuefields \ - union { struct { int tt__; Value v__; } i; double d__; } u -#define NILCONSTANT {{tag2tt(LUA_TNIL), {NULL}}} -/* field-access macros */ -#define v_(o) ((o)->u.i.v__) -#define d_(o) ((o)->u.d__) -#define tt_(o) ((o)->u.i.tt__) - -#endif /* } */ - -#endif /* } */ - - -/* correspondence with standard representation */ -#undef val_ -#define val_(o) v_(o) -#undef num_ -#define num_(o) d_(o) - - -#undef numfield -#define numfield /* no such field; numbers are the entire struct */ - -/* basic check to distinguish numbers from non-numbers */ -#undef ttisnumber -#define ttisnumber(o) ((tt_(o) & NNMASK) != NNMARK) - -#define tag2tt(t) (NNMARK | (t)) - -#undef rttype -#define rttype(o) (ttisnumber(o) ? LUA_TNUMBER : tt_(o) & 0xff) - -#undef settt_ -#define settt_(o,t) (tt_(o) = tag2tt(t)) +typedef TValue *StkId; /* index to stack elements */ -#undef setnvalue -#define setnvalue(obj,x) \ - { TValue *io_=(obj); num_(io_)=(x); lua_assert(ttisnumber(io_)); } -#undef setobj -#define setobj(L,obj1,obj2) \ - { const TValue *o2_=(obj2); TValue *o1_=(obj1); \ - o1_->u = o2_->u; \ - checkliveness(G(L),o1_); } /* -** these redefinitions are not mandatory, but these forms are more efficient +** Header for string value; string bytes follow the end of this structure +** (aligned according to 'UTString'; see next). */ - -#undef checktag -#undef checktype -#define checktag(o,t) (tt_(o) == tag2tt(t)) -#define checktype(o,t) (ctb(tt_(o) | VARBITS) == ctb(tag2tt(t) | VARBITS)) - -#undef ttisequal -#define ttisequal(o1,o2) \ - (ttisnumber(o1) ? ttisnumber(o2) : (tt_(o1) == tt_(o2))) - - -#undef luai_checknum -#define luai_checknum(L,o,c) { if (!ttisnumber(o)) c; } - -#endif -/* }====================================================== */ - +typedef struct TString { + CommonHeader; + lu_byte extra; /* reserved words for short strings; "has hash" for longs */ + lu_byte shrlen; /* length for short strings */ + unsigned int hash; + union { + size_t lnglen; /* length for long strings */ + struct TString *hnext; /* linked list for hash table */ + } u; +} TString; /* -** {====================================================== -** types and prototypes -** ======================================================= +** Ensures that address after this type is always fully aligned. */ +typedef union UTString { + L_Umaxalign dummy; /* ensures maximum alignment for strings */ + TString tsv; +} UTString; -union Value { - GCObject *gc; /* collectable objects */ - void *p; /* light userdata */ - int b; /* booleans */ - lua_CFunction f; /* light C functions */ - numfield /* numbers */ -}; - - -struct lua_TValue { - TValuefields; -}; +/* +** Get the actual string (array of bytes) from a 'TString'. +** (Access to 'extra' ensures that value is really a 'TString'.) +*/ +#define getstr(ts) \ + check_exp(sizeof((ts)->extra), cast(char *, (ts)) + sizeof(UTString)) -typedef TValue *StkId; /* index to stack elements */ +/* get the actual string (array of bytes) from a Lua value */ +#define svalue(o) getstr(tsvalue(o)) +/* get string length from 'TString *s' */ +#define tsslen(s) ((s)->tt == LUA_TSHRSTR ? (s)->shrlen : (s)->u.lnglen) +/* get string length from 'TValue *o' */ +#define vslen(o) tsslen(tsvalue(o)) /* -** Header for string value; string bytes follow the end of this structure +** Header for userdata; memory area follows the end of this structure +** (aligned according to 'UUdata'; see next). */ -typedef union TString { - L_Umaxalign dummy; /* ensures maximum alignment for strings */ - struct { - CommonHeader; - lu_byte extra; /* reserved words for short strings; "has hash" for longs */ - unsigned int hash; - size_t len; /* number of characters in string */ - } tsv; -} TString; - +typedef struct Udata { + CommonHeader; + lu_byte ttuv_; /* user value's tag */ + struct Table *metatable; + size_t len; /* number of bytes */ + union Value user_; /* user value */ +} Udata; -/* get the actual string (array of bytes) from a TString */ -#define getstr(ts) cast(const char *, (ts) + 1) -/* get the actual string (array of bytes) from a Lua value */ -#define svalue(o) getstr(rawtsvalue(o)) +/* +** Ensures that address after this type is always fully aligned. +*/ +typedef union UUdata { + L_Umaxalign dummy; /* ensures maximum alignment for 'local' udata */ + Udata uv; +} UUdata; /* -** Header for userdata; memory area follows the end of this structure +** Get the address of memory block inside 'Udata'. +** (Access to 'ttuv_' ensures that value is really a 'Udata'.) */ -typedef union Udata { - L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ - struct { - CommonHeader; - struct Table *metatable; - struct Table *env; - size_t len; /* number of bytes */ - } uv; -} Udata; +#define getudatamem(u) \ + check_exp(sizeof((u)->ttuv_), (cast(char*, (u)) + sizeof(UUdata))) + +#define setuservalue(L,u,o) \ + { const TValue *io=(o); Udata *iu = (u); \ + iu->user_ = io->value_; iu->ttuv_ = rttype(io); \ + checkliveness(L,io); } +#define getuservalue(L,u,o) \ + { TValue *io=(o); const Udata *iu = (u); \ + io->value_ = iu->user_; settt_(io, iu->ttuv_); \ + checkliveness(L,io); } + /* ** Description of an upvalue for function prototypes */ typedef struct Upvaldesc { TString *name; /* upvalue name (for debug information) */ - lu_byte instack; /* whether it is in stack */ + lu_byte instack; /* whether it is in stack (register) */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ } Upvaldesc; @@ -494,24 +434,15 @@ typedef struct Proto { TValue *k; /* constants used by the function */ struct SharedProto *sp; struct Proto **p; /* functions defined inside the function */ - union Closure *cache; /* last created closure with this prototype */ + struct LClosure *cache; /* last-created closure with this prototype */ GCObject *gclist; } Proto; + /* ** Lua Upvalues */ -typedef struct UpVal { - CommonHeader; - TValue *v; /* points to stack or to its own value */ - union { - TValue value; /* the value (when closed) */ - struct { /* double linked list (when open) */ - struct UpVal *prev; - struct UpVal *next; - } l; - } u; -} UpVal; +typedef struct UpVal UpVal; /* @@ -553,12 +484,19 @@ typedef union Closure { typedef union TKey { struct { TValuefields; - struct Node *next; /* for chaining */ + int next; /* for chaining (offset for next node) */ } nk; TValue tvk; } TKey; +/* copy a value into a key without messing up field 'next' */ +#define setnodekey(L,key,obj) \ + { TKey *k_=(key); const TValue *io_=(obj); \ + k_->nk.value_ = io_->value_; k_->nk.tt_ = io_->tt_; \ + (void)L; checkliveness(L,io_); } + + typedef struct Node { TValue i_val; TKey i_key; @@ -568,19 +506,19 @@ typedef struct Node { typedef struct Table { CommonHeader; lu_byte flags; /* 1<

#include "lopcodes.h" @@ -31,10 +34,17 @@ LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = { "ADD", "SUB", "MUL", - "DIV", "MOD", "POW", + "DIV", + "IDIV", + "BAND", + "BOR", + "BXOR", + "SHL", + "SHR", "UNM", + "BNOT", "NOT", "LEN", "CONCAT", @@ -79,10 +89,17 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ - ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_IDIV */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BAND */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BOR */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BXOR */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SHL */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SHR */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_BNOT */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ diff --git a/deps/lua/lopcodes.h b/deps/lua/lopcodes.h index 51f5791..864b8e4 100644 --- a/deps/lua/lopcodes.h +++ b/deps/lua/lopcodes.h @@ -1,5 +1,5 @@ /* -** $Id: lopcodes.h,v 1.142.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lopcodes.h,v 1.148 2014/10/25 11:50:46 roberto Exp $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ @@ -14,12 +14,12 @@ We assume that instructions are unsigned numbers. All instructions have an opcode in the first 6 bits. Instructions can have the following fields: - `A' : 8 bits - `B' : 9 bits - `C' : 9 bits + 'A' : 8 bits + 'B' : 9 bits + 'C' : 9 bits 'Ax' : 26 bits ('A', 'B', and 'C' together) - `Bx' : 18 bits (`B' and `C' together) - `sBx' : signed Bx + 'Bx' : 18 bits ('B' and 'C' together) + 'sBx' : signed Bx A signed argument is represented in excess K; that is, the number value is the unsigned value minus K. K is exactly the maximum value @@ -58,7 +58,7 @@ enum OpMode {iABC, iABx, iAsBx, iAx}; /* basic instruction format */ */ #if SIZE_Bx < LUAI_BITSINT-1 #define MAXARG_Bx ((1<>1) /* `sBx' is signed */ +#define MAXARG_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */ #else #define MAXARG_Bx MAX_INT #define MAXARG_sBx MAX_INT @@ -76,10 +76,10 @@ enum OpMode {iABC, iABx, iAsBx, iAx}; /* basic instruction format */ #define MAXARG_C ((1<> RK(C) */ OP_UNM,/* A B R(A) := -R(B) */ +OP_BNOT,/* A B R(A) := ~R(B) */ OP_NOT,/* A B R(A) := not R(B) */ OP_LEN,/* A B R(A) := length of R(B) */ OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */ -OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A) + 1 */ +OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ @@ -231,16 +238,16 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ /*=========================================================================== Notes: - (*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then `top' is + (*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then 'top' is set to last_result+1, so next open instruction (OP_CALL, OP_RETURN, - OP_SETLIST) may use `top'. + OP_SETLIST) may use 'top'. (*) In OP_VARARG, if (B == 0) then use actual number of varargs and set top (like in OP_CALL with C == 0). - (*) In OP_RETURN, if (B == 0) then return up to `top'. + (*) In OP_RETURN, if (B == 0) then return up to 'top'. - (*) In OP_SETLIST, if (B == 0) then B = `top'; if (C == 0) then next + (*) In OP_SETLIST, if (B == 0) then B = 'top'; if (C == 0) then next 'instruction' is EXTRAARG(real C). (*) In OP_LOADKX, the next 'instruction' is always EXTRAARG. @@ -248,7 +255,7 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ (*) For comparisons, A specifies what condition the test should accept (true or false). - (*) All `skips' (pc++) assume that next instruction is a jump. + (*) All 'skips' (pc++) assume that next instruction is a jump. ===========================================================================*/ diff --git a/deps/lua/loslib.c b/deps/lua/loslib.c index 052ba17..7dae533 100644 --- a/deps/lua/loslib.c +++ b/deps/lua/loslib.c @@ -1,9 +1,14 @@ /* -** $Id: loslib.c,v 1.40.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: loslib.c,v 1.60 2015/11/19 19:16:22 roberto Exp $ ** Standard Operating System library ** See Copyright Notice in lua.h */ +#define loslib_c +#define LUA_LIB + +#include "lprefix.h" + #include #include @@ -11,9 +16,6 @@ #include #include -#define loslib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -21,60 +23,108 @@ /* +** {================================================================== ** list of valid conversion specifiers for the 'strftime' function +** =================================================================== */ -#if !defined(LUA_STRFTIMEOPTIONS) +#if !defined(LUA_STRFTIMEOPTIONS) /* { */ -#if !defined(LUA_USE_POSIX) +#if defined(LUA_USE_C89) #define LUA_STRFTIMEOPTIONS { "aAbBcdHIjmMpSUwWxXyYz%", "" } -#else +#else /* C99 specification */ #define LUA_STRFTIMEOPTIONS \ - { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "" \ - "", "E", "cCxXyY", \ + { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "", \ + "E", "cCxXyY", \ "O", "deHImMSuUVwWy" } #endif -#endif - +#endif /* } */ +/* }================================================================== */ /* -** By default, Lua uses tmpnam except when POSIX is available, where it -** uses mkstemp. +** {================================================================== +** Configuration for time-related stuff +** =================================================================== */ -#if defined(LUA_USE_MKSTEMP) -#include -#define LUA_TMPNAMBUFSIZE 32 -#define lua_tmpnam(b,e) { \ - strcpy(b, "/tmp/lua_XXXXXX"); \ - e = mkstemp(b); \ - if (e != -1) close(e); \ - e = (e == -1); } -#elif !defined(lua_tmpnam) +#if !defined(l_time_t) /* { */ +/* +** type to represent time_t in Lua +*/ +#define l_timet lua_Integer +#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) -#define LUA_TMPNAMBUFSIZE L_tmpnam -#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +static time_t l_checktime (lua_State *L, int arg) { + lua_Integer t = luaL_checkinteger(L, arg); + luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); + return (time_t)t; +} -#endif +#endif /* } */ +#if !defined(l_gmtime) /* { */ /* ** By default, Lua uses gmtime/localtime, except when POSIX is available, ** where it uses gmtime_r/localtime_r */ -#if defined(LUA_USE_GMTIME_R) + +#if defined(LUA_USE_POSIX) /* { */ #define l_gmtime(t,r) gmtime_r(t,r) #define l_localtime(t,r) localtime_r(t,r) -#elif !defined(l_gmtime) +#else /* }{ */ + +/* ISO C definitions */ +#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) +#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) + +#endif /* } */ + +#endif /* } */ + +/* }================================================================== */ + -#define l_gmtime(t,r) ((void)r, gmtime(t)) -#define l_localtime(t,r) ((void)r, localtime(t)) +/* +** {================================================================== +** Configuration for 'tmpnam': +** By default, Lua uses tmpnam except when POSIX is available, where +** it uses mkstemp. +** =================================================================== +*/ +#if !defined(lua_tmpnam) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include + +#define LUA_TMPNAMBUFSIZE 32 +#if !defined(LUA_TMPNAMTEMPLATE) +#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" #endif +#define lua_tmpnam(b,e) { \ + strcpy(b, LUA_TMPNAMTEMPLATE); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else /* }{ */ + +/* ISO C definitions */ +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } + +#endif /* } */ + +#endif /* } */ +/* }================================================================== */ + + static int os_execute (lua_State *L) { @@ -147,24 +197,35 @@ static void setboolfield (lua_State *L, const char *key, int value) { static int getboolfield (lua_State *L, const char *key) { int res; - lua_getfield(L, -1, key); - res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); + res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); lua_pop(L, 1); return res; } -static int getfield (lua_State *L, const char *key, int d) { - int res, isnum; - lua_getfield(L, -1, key); - res = (int)lua_tointegerx(L, -1, &isnum); - if (!isnum) { - if (d < 0) - return luaL_error(L, "field " LUA_QS " missing in date table", key); +/* maximum value for date fields (to avoid arithmetic overflows with 'int') */ +#if !defined(L_MAXDATEFIELD) +#define L_MAXDATEFIELD (INT_MAX / 2) +#endif + +static int getfield (lua_State *L, const char *key, int d, int delta) { + int isnum; + int t = lua_getfield(L, -1, key); + lua_Integer res = lua_tointegerx(L, -1, &isnum); + if (!isnum) { /* field is not a number? */ + if (t != LUA_TNIL) /* some other value? */ + return luaL_error(L, "field '%s' not an integer", key); + else if (d < 0) /* absent field; no default? */ + return luaL_error(L, "field '%s' missing in date table", key); res = d; } + else { + if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) + return luaL_error(L, "field '%s' out-of-bounds", key); + res -= delta; + } lua_pop(L, 1); - return res; + return (int)res; } @@ -192,19 +253,23 @@ static const char *checkoption (lua_State *L, const char *conv, char *buff) { } +/* maximum size for an individual 'strftime' item */ +#define SIZETIMEFMT 250 + + static int os_date (lua_State *L) { const char *s = luaL_optstring(L, 1, "%c"); - time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); + time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); struct tm tmr, *stm; if (*s == '!') { /* UTC? */ stm = l_gmtime(&t, &tmr); - s++; /* skip `!' */ + s++; /* skip '!' */ } else stm = l_localtime(&t, &tmr); if (stm == NULL) /* invalid date? */ - lua_pushnil(L); - else if (strcmp(s, "*t") == 0) { + luaL_error(L, "time result cannot be represented in this installation"); + if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ setfield(L, "sec", stm->tm_sec); setfield(L, "min", stm->tm_min); @@ -222,14 +287,14 @@ static int os_date (lua_State *L) { cc[0] = '%'; luaL_buffinit(L, &b); while (*s) { - if (*s != '%') /* no conversion specifier? */ + if (*s != '%') /* not a conversion specifier? */ luaL_addchar(&b, *s++); else { size_t reslen; - char buff[200]; /* should be big enough for any conversion result */ + char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); s = checkoption(L, s + 1, cc); - reslen = strftime(buff, sizeof(buff), cc, stm); - luaL_addlstring(&b, buff, reslen); + reslen = strftime(buff, SIZETIMEFMT, cc, stm); + luaL_addsize(&b, reslen); } } luaL_pushresult(&b); @@ -246,26 +311,26 @@ static int os_time (lua_State *L) { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ - ts.tm_sec = getfield(L, "sec", 0); - ts.tm_min = getfield(L, "min", 0); - ts.tm_hour = getfield(L, "hour", 12); - ts.tm_mday = getfield(L, "day", -1); - ts.tm_mon = getfield(L, "month", -1) - 1; - ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_sec = getfield(L, "sec", 0, 0); + ts.tm_min = getfield(L, "min", 0, 0); + ts.tm_hour = getfield(L, "hour", 12, 0); + ts.tm_mday = getfield(L, "day", -1, 0); + ts.tm_mon = getfield(L, "month", -1, 1); + ts.tm_year = getfield(L, "year", -1, 1900); ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); } - if (t == (time_t)(-1)) - lua_pushnil(L); - else - lua_pushnumber(L, (lua_Number)t); + if (t != (time_t)(l_timet)t || t == (time_t)(-1)) + luaL_error(L, "time result cannot be represented in this installation"); + l_pushtime(L, t); return 1; } static int os_difftime (lua_State *L) { - lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), - (time_t)(luaL_optnumber(L, 2, 0)))); + time_t t1 = l_checktime(L, 1); + time_t t2 = l_checktime(L, 2); + lua_pushnumber(L, (lua_Number)difftime(t1, t2)); return 1; } @@ -289,7 +354,7 @@ static int os_exit (lua_State *L) { if (lua_isboolean(L, 1)) status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); else - status = luaL_optint(L, 1, EXIT_SUCCESS); + status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); if (lua_toboolean(L, 2)) lua_close(L); if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ diff --git a/deps/lua/lparser.c b/deps/lua/lparser.c index a8e37fd..880e5c2 100644 --- a/deps/lua/lparser.c +++ b/deps/lua/lparser.c @@ -1,15 +1,17 @@ /* -** $Id: lparser.c,v 2.130.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lparser.c,v 2.149 2015/11/02 16:09:30 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ - -#include - #define lparser_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "lcode.h" @@ -35,17 +37,21 @@ #define hasmultret(k) ((k) == VCALL || (k) == VVARARG) +/* because all strings are unified by the scanner, the parser + can use pointer equality for string equality */ +#define eqstr(a,b) ((a) == (b)) + /* ** nodes for block list (list of active blocks) */ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ - short firstlabel; /* index of first label in this block */ - short firstgoto; /* index of first pending goto in this block */ + int firstlabel; /* index of first label in this block */ + int firstgoto; /* index of first pending goto in this block */ lu_byte nactvar; /* # active locals outside the block */ lu_byte upval; /* true if some variable in the block is an upvalue */ - lu_byte isloop; /* true if `block' is a loop */ + lu_byte isloop; /* true if 'block' is a loop */ } BlockCnt; @@ -57,19 +63,9 @@ static void statement (LexState *ls); static void expr (LexState *ls, expdesc *v); -static void anchor_token (LexState *ls) { - /* last token from outer function must be EOS */ - lua_assert(ls->fs != NULL || ls->t.token == TK_EOS); - if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { - TString *ts = ls->t.seminfo.ts; - luaX_newstring(ls, getstr(ts), ts->tsv.len); - } -} - - /* semantic error */ static l_noret semerror (LexState *ls, const char *msg) { - ls->t.token = 0; /* remove 'near to' from final message */ + ls->t.token = 0; /* remove "near " from final message */ luaX_syntaxerror(ls, msg); } @@ -223,7 +219,7 @@ static int searchupvalue (FuncState *fs, TString *name) { int i; Upvaldesc *up = fs->f->sp->upvalues; for (i = 0; i < fs->nups; i++) { - if (luaS_eqstr(up[i].name, name)) return i; + if (eqstr(up[i].name, name)) return i; } return -1; /* not found */ } @@ -248,7 +244,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { static int searchvar (FuncState *fs, TString *n) { int i; for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { - if (luaS_eqstr(n, getlocvar(fs, i)->varname)) + if (eqstr(n, getlocvar(fs, i)->varname)) return i; } return -1; /* not found */ @@ -344,11 +340,11 @@ static void closegoto (LexState *ls, int g, Labeldesc *label) { FuncState *fs = ls->fs; Labellist *gl = &ls->dyd->gt; Labeldesc *gt = &gl->arr[g]; - lua_assert(luaS_eqstr(gt->name, label->name)); + lua_assert(eqstr(gt->name, label->name)); if (gt->nactvar < label->nactvar) { TString *vname = getlocvar(fs, gt->nactvar)->varname; const char *msg = luaO_pushfstring(ls->L, - " at line %d jumps into the scope of local " LUA_QS, + " at line %d jumps into the scope of local '%s'", getstr(gt->name), gt->line, getstr(vname)); semerror(ls, msg); } @@ -371,7 +367,7 @@ static int findlabel (LexState *ls, int g) { /* check labels in current block for a match */ for (i = bl->firstlabel; i < dyd->label.n; i++) { Labeldesc *lb = &dyd->label.arr[i]; - if (luaS_eqstr(lb->name, gt->name)) { /* correct label? */ + if (eqstr(lb->name, gt->name)) { /* correct label? */ if (gt->nactvar > lb->nactvar && (bl->upval || dyd->label.n > bl->firstlabel)) luaK_patchclose(ls->fs, gt->pc, lb->nactvar); @@ -392,7 +388,7 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name, l->arr[n].line = line; l->arr[n].nactvar = ls->fs->nactvar; l->arr[n].pc = pc; - l->n++; + l->n = n + 1; return n; } @@ -405,7 +401,7 @@ static void findgotos (LexState *ls, Labeldesc *lb) { Labellist *gl = &ls->dyd->gt; int i = ls->fs->bl->firstgoto; while (i < gl->n) { - if (luaS_eqstr(gl->arr[i].name, lb->name)) + if (eqstr(gl->arr[i].name, lb->name)) closegoto(ls, i, lb); else i++; @@ -414,7 +410,7 @@ static void findgotos (LexState *ls, Labeldesc *lb) { /* -** "export" pending gotos to outer level, to check them against +** export pending gotos to outer level, to check them against ** outer labels; if the block being exited has upvalues, and ** the goto exits the scope of any variable (which can be the ** upvalue), close those variables being exited. @@ -450,7 +446,7 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { /* -** create a label named "break" to resolve break statements +** create a label named 'break' to resolve break statements */ static void breaklabel (LexState *ls) { TString *n = luaS_new(ls->L, "break"); @@ -465,7 +461,7 @@ static void breaklabel (LexState *ls) { static l_noret undefgoto (LexState *ls, Labeldesc *gt) { const char *msg = isreserved(gt->name) ? "<%s> at line %d not inside a loop" - : "no visible label " LUA_QS " for at line %d"; + : "no visible label '%s' for at line %d"; msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); semerror(ls, msg); } @@ -527,7 +523,6 @@ static void codeclosure (LexState *ls, expdesc *v) { static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { - lua_State *L = ls->L; SharedProto *f; fs->prev = ls->fs; /* linked list of funcstates */ fs->ls = ls; @@ -546,10 +541,6 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { f = fs->f->sp; f->source = ls->source; f->maxstacksize = 2; /* registers 0/1 are always valid */ - fs->h = luaH_new(L); - /* anchor table of constants (to avoid being collected) */ - sethvalue2s(L, L->top, fs->h); - incr_top(L); enterblock(fs, bl, 0); } @@ -575,9 +566,6 @@ static void close_func (LexState *ls) { sp->sizeupvalues = fs->nups; lua_assert(fs->bl == NULL); ls->fs = fs->prev; - /* last token read was anchored in defunct function; must re-anchor it */ - anchor_token(ls); - L->top--; /* pop table of constants */ luaC_checkGC(L); } @@ -591,7 +579,7 @@ static void close_func (LexState *ls) { /* ** check whether current token is in the follow set of a block. ** 'until' closes syntactical blocks, but do not close scope, -** so it handled in separate. +** so it is handled in separate. */ static int block_follow (LexState *ls, int withuntil) { switch (ls->t.token) { @@ -605,7 +593,7 @@ static int block_follow (LexState *ls, int withuntil) { static void statlist (LexState *ls) { - /* statlist -> { stat [`;'] } */ + /* statlist -> { stat [';'] } */ while (!block_follow(ls, 1)) { if (ls->t.token == TK_RETURN) { statement(ls); @@ -646,14 +634,14 @@ static void yindex (LexState *ls, expdesc *v) { struct ConsControl { expdesc v; /* last list item read */ expdesc *t; /* table descriptor */ - int nh; /* total number of `record' elements */ + int nh; /* total number of 'record' elements */ int na; /* total number of array elements */ int tostore; /* number of array elements pending to be stored */ }; static void recfield (LexState *ls, struct ConsControl *cc) { - /* recfield -> (NAME | `['exp1`]') = exp1 */ + /* recfield -> (NAME | '['exp1']') = exp1 */ FuncState *fs = ls->fs; int reg = ls->fs->freereg; expdesc key, val; @@ -760,12 +748,12 @@ static void constructor (LexState *ls, expdesc *t) { static void parlist (LexState *ls) { - /* parlist -> [ param { `,' param } ] */ + /* parlist -> [ param { ',' param } ] */ FuncState *fs = ls->fs; SharedProto *f = fs->f->sp; int nparams = 0; f->is_vararg = 0; - if (ls->t.token != ')') { /* is `parlist' not empty? */ + if (ls->t.token != ')') { /* is 'parlist' not empty? */ do { switch (ls->t.token) { case TK_NAME: { /* param -> NAME */ @@ -773,12 +761,12 @@ static void parlist (LexState *ls) { nparams++; break; } - case TK_DOTS: { /* param -> `...' */ + case TK_DOTS: { /* param -> '...' */ luaX_next(ls); - f->is_vararg = 1; + f->is_vararg = 2; /* declared vararg */ break; } - default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); + default: luaX_syntaxerror(ls, " or '...' expected"); } } while (!f->is_vararg && testnext(ls, ',')); } @@ -789,7 +777,7 @@ static void parlist (LexState *ls) { static void body (LexState *ls, expdesc *e, int ismethod, int line) { - /* body -> `(' parlist `)' block END */ + /* body -> '(' parlist ')' block END */ FuncState new_fs; BlockCnt bl; new_fs.f = addprototype(ls); @@ -811,7 +799,7 @@ static void body (LexState *ls, expdesc *e, int ismethod, int line) { static int explist (LexState *ls, expdesc *v) { - /* explist -> expr { `,' expr } */ + /* explist -> expr { ',' expr } */ int n = 1; /* at least one expression */ expr(ls, v); while (testnext(ls, ',')) { @@ -828,7 +816,7 @@ static void funcargs (LexState *ls, expdesc *f, int line) { expdesc args; int base, nparams; switch (ls->t.token) { - case '(': { /* funcargs -> `(' [ explist ] `)' */ + case '(': { /* funcargs -> '(' [ explist ] ')' */ luaX_next(ls); if (ls->t.token == ')') /* arg list is empty? */ args.k = VVOID; @@ -845,7 +833,7 @@ static void funcargs (LexState *ls, expdesc *f, int line) { } case TK_STRING: { /* funcargs -> STRING */ codestring(ls, &args, ls->t.seminfo.ts); - luaX_next(ls); /* must use `seminfo' before `next' */ + luaX_next(ls); /* must use 'seminfo' before 'next' */ break; } default: { @@ -911,14 +899,14 @@ static void suffixedexp (LexState *ls, expdesc *v) { fieldsel(ls, v); break; } - case '[': { /* `[' exp1 `]' */ + case '[': { /* '[' exp1 ']' */ expdesc key; luaK_exp2anyregup(fs, v); yindex(ls, &key); luaK_indexed(fs, v, &key); break; } - case ':': { /* `:' NAME funcargs */ + case ':': { /* ':' NAME funcargs */ expdesc key; luaX_next(ls); checkname(ls, &key); @@ -938,14 +926,19 @@ static void suffixedexp (LexState *ls, expdesc *v) { static void simpleexp (LexState *ls, expdesc *v) { - /* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... | + /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | constructor | FUNCTION body | suffixedexp */ switch (ls->t.token) { - case TK_NUMBER: { - init_exp(v, VKNUM, 0); + case TK_FLT: { + init_exp(v, VKFLT, 0); v->u.nval = ls->t.seminfo.r; break; } + case TK_INT: { + init_exp(v, VKINT, 0); + v->u.ival = ls->t.seminfo.i; + break; + } case TK_STRING: { codestring(ls, v, ls->t.seminfo.ts); break; @@ -965,7 +958,8 @@ static void simpleexp (LexState *ls, expdesc *v) { case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; check_condition(ls, fs->f->sp->is_vararg, - "cannot use " LUA_QL("...") " outside a vararg function"); + "cannot use '...' outside a vararg function"); + fs->f->sp->is_vararg = 1; /* function actually uses vararg */ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); break; } @@ -991,6 +985,7 @@ static UnOpr getunopr (int op) { switch (op) { case TK_NOT: return OPR_NOT; case '-': return OPR_MINUS; + case '~': return OPR_BNOT; case '#': return OPR_LEN; default: return OPR_NOUNOPR; } @@ -1002,9 +997,15 @@ static BinOpr getbinopr (int op) { case '+': return OPR_ADD; case '-': return OPR_SUB; case '*': return OPR_MUL; - case '/': return OPR_DIV; case '%': return OPR_MOD; case '^': return OPR_POW; + case '/': return OPR_DIV; + case TK_IDIV: return OPR_IDIV; + case '&': return OPR_BAND; + case '|': return OPR_BOR; + case '~': return OPR_BXOR; + case TK_SHL: return OPR_SHL; + case TK_SHR: return OPR_SHR; case TK_CONCAT: return OPR_CONCAT; case TK_NE: return OPR_NE; case TK_EQ: return OPR_EQ; @@ -1023,19 +1024,24 @@ static const struct { lu_byte left; /* left priority for each binary operator */ lu_byte right; /* right priority */ } priority[] = { /* ORDER OPR */ - {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `*' `/' `%' */ - {10, 9}, {5, 4}, /* ^, .. (right associative) */ - {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ - {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ - {2, 2}, {1, 1} /* and, or */ + {10, 10}, {10, 10}, /* '+' '-' */ + {11, 11}, {11, 11}, /* '*' '%' */ + {14, 13}, /* '^' (right associative) */ + {11, 11}, {11, 11}, /* '/' '//' */ + {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */ + {7, 7}, {7, 7}, /* '<<' '>>' */ + {9, 8}, /* '..' (right associative) */ + {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ + {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ + {2, 2}, {1, 1} /* and, or */ }; -#define UNARY_PRIORITY 8 /* priority for unary operators */ +#define UNARY_PRIORITY 12 /* priority for unary operators */ /* ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } -** where `binop' is any binary operator with a priority higher than `limit' +** where 'binop' is any binary operator with a priority higher than 'limit' */ static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { BinOpr op; @@ -1049,7 +1055,7 @@ static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { luaK_prefix(ls->fs, uop, v, line); } else simpleexp(ls, v); - /* expand while operators have priorities higher than `limit' */ + /* expand while operators have priorities higher than 'limit' */ op = getbinopr(ls->t.token); while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2; @@ -1149,7 +1155,7 @@ static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { "C levels"); assignment(ls, &nv, nvars+1); } - else { /* assignment -> `=' explist */ + else { /* assignment -> '=' explist */ int nexps; checknext(ls, '='); nexps = explist(ls, &e); @@ -1173,7 +1179,7 @@ static int cond (LexState *ls) { /* cond -> exp */ expdesc v; expr(ls, &v); /* read condition */ - if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ + if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */ luaK_goiftrue(ls->fs, &v); return v.f; } @@ -1198,9 +1204,9 @@ static void gotostat (LexState *ls, int pc) { static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) { int i; for (i = fs->bl->firstlabel; i < ll->n; i++) { - if (luaS_eqstr(label, ll->arr[i].name)) { + if (eqstr(label, ll->arr[i].name)) { const char *msg = luaO_pushfstring(fs->ls->L, - "label " LUA_QS " already defined on line %d", + "label '%s' already defined on line %d", getstr(label), ll->arr[i].line); semerror(fs->ls, msg); } @@ -1324,7 +1330,7 @@ static void fornum (LexState *ls, TString *varname, int line) { if (testnext(ls, ',')) exp1(ls); /* optional step */ else { /* default step = 1 */ - luaK_codek(fs, fs->freereg, luaK_numberK(fs, 1)); + luaK_codek(fs, fs->freereg, luaK_intK(fs, 1)); luaK_reserveregs(fs, 1); } forbody(ls, base, line, 1, 1); @@ -1362,15 +1368,15 @@ static void forstat (LexState *ls, int line) { TString *varname; BlockCnt bl; enterblock(fs, &bl, 1); /* scope for loop and control variables */ - luaX_next(ls); /* skip `for' */ + luaX_next(ls); /* skip 'for' */ varname = str_checkname(ls); /* first variable name */ switch (ls->t.token) { case '=': fornum(ls, varname, line); break; case ',': case TK_IN: forlist(ls, varname); break; - default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); + default: luaX_syntaxerror(ls, "'=' or 'in' expected"); } check_match(ls, TK_END, TK_FOR, line); - leaveblock(fs); /* loop scope (`break' jumps to this point) */ + leaveblock(fs); /* loop scope ('break' jumps to this point) */ } @@ -1400,7 +1406,7 @@ static void test_then_block (LexState *ls, int *escapelist) { enterblock(fs, &bl, 0); jf = v.f; } - statlist(ls); /* `then' part */ + statlist(ls); /* 'then' part */ leaveblock(fs); if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ @@ -1417,7 +1423,7 @@ static void ifstat (LexState *ls, int line) { while (ls->t.token == TK_ELSEIF) test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ if (testnext(ls, TK_ELSE)) - block(ls); /* `else' part */ + block(ls); /* 'else' part */ check_match(ls, TK_END, TK_IF, line); luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ } @@ -1435,7 +1441,7 @@ static void localfunc (LexState *ls) { static void localstat (LexState *ls) { - /* stat -> LOCAL NAME {`,' NAME} [`=' explist] */ + /* stat -> LOCAL NAME {',' NAME} ['=' explist] */ int nvars = 0; int nexps; expdesc e; @@ -1455,7 +1461,7 @@ static void localstat (LexState *ls) { static int funcname (LexState *ls, expdesc *v) { - /* funcname -> NAME {fieldsel} [`:' NAME] */ + /* funcname -> NAME {fieldsel} [':' NAME] */ int ismethod = 0; singlevar(ls, v); while (ls->t.token == '.') @@ -1476,7 +1482,7 @@ static void funcstat (LexState *ls, int line) { ismethod = funcname(ls, &v); body(ls, &b, ismethod, line); luaK_storevar(ls->fs, &v, &b); - luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ + luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ } @@ -1518,8 +1524,8 @@ static void retstat (LexState *ls) { if (nret == 1) /* only one single value? */ first = luaK_exp2anyreg(fs, &e); else { - luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ - first = fs->nactvar; /* return all `active' values */ + luaK_exp2nextreg(fs, &e); /* values must go to the stack */ + first = fs->nactvar; /* return all active values */ lua_assert(nret == fs->freereg - first); } } @@ -1591,7 +1597,7 @@ static void statement (LexState *ls) { break; } } - lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + lua_assert(ls->fs->f->sp->maxstacksize >= ls->fs->freereg && ls->fs->freereg >= ls->fs->nactvar); ls->fs->freereg = ls->fs->nactvar; /* free registers */ leavelevel(ls); @@ -1608,7 +1614,7 @@ static void mainfunc (LexState *ls, FuncState *fs) { BlockCnt bl; expdesc v; open_func(ls, fs, &bl); - fs->f->sp->is_vararg = 1; /* main function is always vararg */ + fs->f->sp->is_vararg = 2; /* main function is always declared vararg */ init_exp(&v, VLOCAL, 0); /* create and... */ newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ luaX_next(ls); /* read first token */ @@ -1618,16 +1624,19 @@ static void mainfunc (LexState *ls, FuncState *fs) { } -Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, - Dyndata *dyd, const char *name, int firstchar) { +LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar) { LexState lexstate; FuncState funcstate; - Closure *cl = luaF_newLclosure(L, 1); /* create main closure */ - /* anchor closure (to avoid being collected) */ - setclLvalue(L, L->top, cl); - incr_top(L); - funcstate.f = cl->l.p = luaF_newproto(L, NULL); + LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ + setclLvalue(L, L->top, cl); /* anchor it (to avoid being collected) */ + luaD_inctop(L); + lexstate.h = luaH_new(L); /* create table for scanner */ + sethvalue(L, L->top, lexstate.h); /* anchor it */ + luaD_inctop(L); + funcstate.f = cl->p = luaF_newproto(L, NULL); funcstate.f->sp->source = luaS_new(L, name); /* create and anchor TString */ + lua_assert(iswhite(funcstate.f)); /* do not need barrier here */ lexstate.buff = buff; lexstate.dyd = dyd; dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; @@ -1636,6 +1645,7 @@ Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); /* all scopes should be correctly finished */ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); - return cl; /* it's on the stack too */ + L->top--; /* remove scanner's table */ + return cl; /* closure is on the stack, too */ } diff --git a/deps/lua/lparser.h b/deps/lua/lparser.h index 0346e3c..62c50ca 100644 --- a/deps/lua/lparser.h +++ b/deps/lua/lparser.h @@ -1,5 +1,5 @@ /* -** $Id: lparser.h,v 1.70.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lparser.h,v 1.74 2014/10/25 11:50:46 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -21,8 +21,9 @@ typedef enum { VNIL, VTRUE, VFALSE, - VK, /* info = index of constant in `k' */ - VKNUM, /* nval = numerical value */ + VK, /* info = index of constant in 'k' */ + VKFLT, /* nval = numerical float value */ + VKINT, /* nval = numerical integer value */ VNONRELOC, /* info = result register */ VLOCAL, /* info = local register */ VUPVAL, /* info = index of upvalue in 'upvalues' */ @@ -46,10 +47,11 @@ typedef struct expdesc { lu_byte vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */ } ind; int info; /* for generic use */ - lua_Number nval; /* for VKNUM */ + lua_Number nval; /* for VKFLT */ + lua_Integer ival; /* for VKINT */ } u; - int t; /* patch list of `exit when true' */ - int f; /* patch list of `exit when false' */ + int t; /* patch list of 'exit when true' */ + int f; /* patch list of 'exit when false' */ } expdesc; @@ -95,15 +97,14 @@ struct BlockCnt; /* defined in lparser.c */ /* state needed to generate code for a given function */ typedef struct FuncState { Proto *f; /* current function header */ - Table *h; /* table to find (and reuse) elements in `k' */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct BlockCnt *bl; /* chain of current blocks */ - int pc; /* next position to code (equivalent to `ncode') */ + int pc; /* next position to code (equivalent to 'ncode') */ int lasttarget; /* 'label' of last 'jump label' */ - int jpc; /* list of pending jumps to `pc' */ - int nk; /* number of elements in `k' */ - int np; /* number of elements in `p' */ + int jpc; /* list of pending jumps to 'pc' */ + int nk; /* number of elements in 'k' */ + int np; /* number of elements in 'p' */ int firstlocal; /* index of first local var (in Dyndata array) */ short nlocvars; /* number of elements in 'f->locvars' */ lu_byte nactvar; /* number of active local variables */ @@ -112,8 +113,8 @@ typedef struct FuncState { } FuncState; -LUAI_FUNC Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, - Dyndata *dyd, const char *name, int firstchar); +LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar); #endif diff --git a/deps/lua/lprefix.h b/deps/lua/lprefix.h new file mode 100644 index 0000000..02daa83 --- /dev/null +++ b/deps/lua/lprefix.h @@ -0,0 +1,45 @@ +/* +** $Id: lprefix.h,v 1.2 2014/12/29 16:54:13 roberto Exp $ +** Definitions for Lua code that must come before any other header file +** See Copyright Notice in lua.h +*/ + +#ifndef lprefix_h +#define lprefix_h + + +/* +** Allows POSIX/XSI stuff +*/ +#if !defined(LUA_USE_C89) /* { */ + +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 600 +#elif _XOPEN_SOURCE == 0 +#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ +#endif + +/* +** Allows manipulation of large files in gcc and some other compilers +*/ +#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#endif + +#endif /* } */ + + +/* +** Windows stuff +*/ +#if defined(_WIN32) /* { */ + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ +#endif + +#endif /* } */ + +#endif + diff --git a/deps/lua/lstate.c b/deps/lua/lstate.c index c7f2672..9194ac3 100644 --- a/deps/lua/lstate.c +++ b/deps/lua/lstate.c @@ -1,16 +1,18 @@ /* -** $Id: lstate.c,v 2.99.1.2 2013/11/08 17:45:31 roberto Exp $ +** $Id: lstate.c,v 2.133 2015/11/13 12:16:51 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ +#define lstate_c +#define LUA_CORE + +#include "lprefix.h" + #include #include -#define lstate_c -#define LUA_CORE - #include "lua.h" #include "lapi.h" @@ -30,18 +32,11 @@ #define LUAI_GCPAUSE 200 /* 200% */ #endif -#if !defined(LUAI_GCMAJOR) -#define LUAI_GCMAJOR 200 /* 200% */ -#endif - #if !defined(LUAI_GCMUL) #define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ #endif -#define MEMERRMSG "not enough memory" - - /* ** a macro to help the creation of a unique random seed when a state is ** created; the seed is used to randomize hashes. @@ -57,9 +52,7 @@ ** thread state + extra space */ typedef struct LX { -#if defined(LUAI_EXTRASPACE) - char buff[LUAI_EXTRASPACE]; -#endif + lu_byte extra_[LUA_EXTRASPACE]; lua_State l; } LX; @@ -78,13 +71,12 @@ typedef struct LG { /* -** Compute an initial seed as random as possible. In ANSI, rely on -** Address Space Layout Randomization (if present) to increase -** randomness.. +** Compute an initial seed as random as possible. Rely on Address Space +** Layout Randomization (if present) to increase randomness.. */ #define addbuff(b,p,e) \ { size_t t = cast(size_t, e); \ - memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); } + memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } static unsigned int makeseed (lua_State *L) { char buff[4 * sizeof(size_t)]; @@ -101,10 +93,14 @@ static unsigned int makeseed (lua_State *L) { /* ** set GCdebt to a new value keeping the value (totalbytes + GCdebt) -** invariant +** invariant (and avoiding underflows in 'totalbytes') */ void luaE_setdebt (global_State *g, l_mem debt) { - g->totalbytes -= (debt - g->GCdebt); + l_mem tb = gettotalbytes(g); + lua_assert(tb > 0); + if (debt < tb - MAX_LMEM) + debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ + g->totalbytes = tb - debt; g->GCdebt = debt; } @@ -115,10 +111,14 @@ CallInfo *luaE_extendCI (lua_State *L) { L->ci->next = ci; ci->previous = L->ci; ci->next = NULL; + L->nci++; return ci; } +/* +** free all CallInfo structures not in use by a thread +*/ void luaE_freeCI (lua_State *L) { CallInfo *ci = L->ci; CallInfo *next = ci->next; @@ -126,6 +126,24 @@ void luaE_freeCI (lua_State *L) { while ((ci = next) != NULL) { next = ci->next; luaM_free(L, ci); + L->nci--; + } +} + + +/* +** free half of the CallInfo structures not in use by a thread +*/ +void luaE_shrinkCI (lua_State *L) { + CallInfo *ci = L->ci; + CallInfo *next2; /* next's next */ + /* while there are two nexts */ + while (ci->next != NULL && (next2 = ci->next->next) != NULL) { + luaM_free(L, ci->next); /* free next */ + L->nci--; + ci->next = next2; /* remove 'next' from the list */ + next2->previous = ci; + ci = next2; /* keep next's next */ } } @@ -155,6 +173,7 @@ static void freestack (lua_State *L) { return; /* stack not completely built yet */ L->ci = &L->base_ci; /* free the entire 'ci' list */ luaE_freeCI(L); + lua_assert(L->nci == 0); luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ } @@ -163,34 +182,32 @@ static void freestack (lua_State *L) { ** Create registry table and its predefined values */ static void init_registry (lua_State *L, global_State *g) { - TValue mt; + TValue temp; /* create registry */ Table *registry = luaH_new(L); sethvalue(L, &g->l_registry, registry); luaH_resize(L, registry, LUA_RIDX_LAST, 0); /* registry[LUA_RIDX_MAINTHREAD] = L */ - setthvalue(L, &mt, L); - luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &mt); + setthvalue(L, &temp, L); /* temp = L */ + luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp); /* registry[LUA_RIDX_GLOBALS] = table of globals */ - sethvalue(L, &mt, luaH_new(L)); - luaH_setint(L, registry, LUA_RIDX_GLOBALS, &mt); + sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */ + luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp); } /* -** open parts of the state that may cause memory-allocation errors +** open parts of the state that may cause memory-allocation errors. +** ('g->version' != NULL flags that the state was completely build) */ static void f_luaopen (lua_State *L, void *ud) { global_State *g = G(L); UNUSED(ud); stack_init(L, L); /* init stack */ init_registry(L, g); - luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + luaS_init(L); luaT_init(L); luaX_init(L); - /* pre-create memory-error message */ - g->memerrmsg = luaS_newliteral(L, MEMERRMSG); - luaS_fix(g->memerrmsg); /* it should never be collected */ g->gcrunning = 1; /* allow gc */ g->version = lua_version(NULL); luai_userstateopen(L); @@ -198,14 +215,16 @@ static void f_luaopen (lua_State *L, void *ud) { /* -** preinitialize a state with consistent values without allocating +** preinitialize a thread with consistent values without allocating ** any memory (to avoid errors) */ -static void preinit_state (lua_State *L, global_State *g) { +static void preinit_thread (lua_State *L, global_State *g) { G(L) = g; L->stack = NULL; L->ci = NULL; + L->nci = 0; L->stacksize = 0; + L->twups = L; /* thread has no upvalues */ L->errorJmp = NULL; L->nCcalls = 0; L->hook = NULL; @@ -227,7 +246,6 @@ static void close_state (lua_State *L) { if (g->version) /* closing a fully built state? */ luai_userstateclose(L); luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); - luaZ_freebuffer(L, &g->buff); freestack(L); lua_assert(gettotalbytes(g) == sizeof(LG)); (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ @@ -235,17 +253,28 @@ static void close_state (lua_State *L) { LUA_API lua_State *lua_newthread (lua_State *L) { + global_State *g = G(L); lua_State *L1; lua_lock(L); luaC_checkGC(L); - L1 = &luaC_newobj(L, LUA_TTHREAD, sizeof(LX), NULL, offsetof(LX, l))->th; + /* create new thread */ + L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; + L1->marked = luaC_white(g); + L1->tt = LUA_TTHREAD; + /* link it on list 'allgc' */ + L1->next = g->allgc; + g->allgc = obj2gco(L1); + /* anchor it on L stack */ setthvalue(L, L->top, L1); api_incr_top(L); - preinit_state(L1, G(L)); + preinit_thread(L1, g); L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); + /* initialize L1 extra space */ + memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), + LUA_EXTRASPACE); luai_userstatethread(L, L1); stack_init(L1, L); /* init stack */ lua_unlock(L); @@ -273,36 +302,31 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g = &l->g; L->next = NULL; L->tt = LUA_TTHREAD; - g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); + g->currentwhite = bitmask(WHITE0BIT); L->marked = luaC_white(g); - g->gckind = KGC_NORMAL; - preinit_state(L, g); + preinit_thread(L, g); g->frealloc = f; g->ud = ud; g->mainthread = L; g->seed = makeseed(L); - g->uvhead.u.l.prev = &g->uvhead; - g->uvhead.u.l.next = &g->uvhead; g->gcrunning = 0; /* no GC while building state */ g->GCestimate = 0; - g->strt.size = 0; - g->strt.nuse = 0; + g->strt.size = g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(&g->l_registry); - luaZ_initbuffer(L, &g->buff); g->panic = NULL; g->version = NULL; g->gcstate = GCSpause; - g->allgc = NULL; - g->finobj = NULL; - g->tobefnz = NULL; - g->sweepgc = g->sweepfin = NULL; + g->gckind = KGC_NORMAL; + g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL; + g->sweepgc = NULL; g->gray = g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; + g->twups = NULL; g->totalbytes = sizeof(LG); g->GCdebt = 0; + g->gcfinnum = 0; g->gcpause = LUAI_GCPAUSE; - g->gcmajorinc = LUAI_GCMAJOR; g->gcstepmul = LUAI_GCMUL; for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { diff --git a/deps/lua/lstate.h b/deps/lua/lstate.h index daffd9a..65c914d 100644 --- a/deps/lua/lstate.h +++ b/deps/lua/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.82.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lstate.h,v 2.128 2015/11/13 12:16:51 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ @@ -16,25 +16,16 @@ /* -** Some notes about garbage-collected objects: All objects in Lua must -** be kept somehow accessible until being freed. +** Some notes about garbage-collected objects: All objects in Lua must +** be kept somehow accessible until being freed, so all objects always +** belong to one (and only one) of these lists, using field 'next' of +** the 'CommonHeader' for the link: ** -** Lua keeps most objects linked in list g->allgc. The link uses field -** 'next' of the CommonHeader. -** -** Strings are kept in several lists headed by the array g->strt.hash. -** -** Open upvalues are not subject to independent garbage collection. They -** are collected together with their respective threads. Lua keeps a -** double-linked list with all open upvalues (g->uvhead) so that it can -** mark objects referred by them. (They are always gray, so they must -** be remarked in the atomic step. Usually their contents would be marked -** when traversing the respective threads, but the thread may already be -** dead, while the upvalue is still accessible through closures.) -** -** Objects with finalizers are kept in the list g->finobj. -** -** The list g->tobefnz links all objects being finalized. +** 'allgc': all objects not marked for finalization; +** 'finobj': all objects marked for finalization; +** 'tobefnz': all objects ready to be finalized; +** 'fixedgc': all objects that are not to be collected (currently +** only small strings, such as reserved words). */ @@ -53,66 +44,72 @@ struct lua_longjmp; /* defined in ldo.c */ /* kinds of Garbage Collection */ #define KGC_NORMAL 0 #define KGC_EMERGENCY 1 /* gc was forced by an allocation failure */ -#define KGC_GEN 2 /* generational collection */ typedef struct stringtable { - GCObject **hash; - lu_int32 nuse; /* number of elements */ + TString **hash; + int nuse; /* number of elements */ int size; } stringtable; /* -** information about a call +** Information about a call. +** When a thread yields, 'func' is adjusted to pretend that the +** top function has only the yielded values in its stack; in that +** case, the actual 'func' value is saved in field 'extra'. +** When a function calls another with a continuation, 'extra' keeps +** the function index so that, in case of errors, the continuation +** function can be called with the correct top. */ typedef struct CallInfo { StkId func; /* function index in the stack */ StkId top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ - short nresults; /* expected number of results from this function */ - lu_byte callstatus; - ptrdiff_t extra; union { struct { /* only for Lua functions */ StkId base; /* base for this function */ const Instruction *savedpc; } l; struct { /* only for C functions */ - int ctx; /* context info. in case of yields */ - lua_CFunction k; /* continuation in case of yields */ + lua_KFunction k; /* continuation in case of yields */ ptrdiff_t old_errfunc; - lu_byte old_allowhook; - lu_byte status; + lua_KContext ctx; /* context info. in case of yields */ } c; } u; + ptrdiff_t extra; + short nresults; /* expected number of results from this function */ + lu_byte callstatus; } CallInfo; /* ** Bits in CallInfo status */ -#define CIST_LUA (1<<0) /* call is running a Lua function */ -#define CIST_HOOKED (1<<1) /* call is running a debug hook */ -#define CIST_REENTRY (1<<2) /* call is running on same invocation of - luaV_execute of previous call */ -#define CIST_YIELDED (1<<3) /* call reentered after suspension */ +#define CIST_OAH (1<<0) /* original value of 'allowhook' */ +#define CIST_LUA (1<<1) /* call is running a Lua function */ +#define CIST_HOOKED (1<<2) /* call is running a debug hook */ +#define CIST_FRESH (1<<3) /* call is running on a fresh invocation + of luaV_execute */ #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ -#define CIST_STAT (1<<5) /* call has an error status (pcall) */ -#define CIST_TAIL (1<<6) /* call was tail called */ -#define CIST_HOOKYIELD (1<<7) /* last hook called yielded */ - +#define CIST_TAIL (1<<5) /* call was tail called */ +#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ +#define CIST_LEQ (1<<7) /* using __lt for __le */ #define isLua(ci) ((ci)->callstatus & CIST_LUA) +/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */ +#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v)) +#define getoah(st) ((st) & CIST_OAH) + /* -** `global state', shared by all threads of this state +** 'global state', shared by all threads of this state */ typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ - void *ud; /* auxiliary data to `frealloc' */ - lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */ + void *ud; /* auxiliary data to 'frealloc' */ + l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ lu_mem GCmemtrav; /* memory traversed by the GC */ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ @@ -123,36 +120,36 @@ typedef struct global_State { lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ lu_byte gcrunning; /* true if GC is running */ - int sweepstrgc; /* position of sweep in `strt' */ GCObject *allgc; /* list of all collectable objects */ + GCObject **sweepgc; /* current position of sweep in list */ GCObject *finobj; /* list of collectable objects with finalizers */ - GCObject **sweepgc; /* current position of sweep in list 'allgc' */ - GCObject **sweepfin; /* current position of sweep in list 'finobj' */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *weak; /* list of tables with weak values */ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ GCObject *allweak; /* list of all-weak tables */ GCObject *tobefnz; /* list of userdata to be GC */ - UpVal uvhead; /* head of double-linked list of all open upvalues */ - Mbuffer buff; /* temporary buffer for string concatenation */ + GCObject *fixedgc; /* list of objects not to be collected */ + struct lua_State *twups; /* list of threads with open upvalues */ + unsigned int gcfinnum; /* number of finalizers to call in each GC step */ int gcpause; /* size of pause between successive GCs */ - int gcmajorinc; /* pause between major collections (only in gen. mode) */ - int gcstepmul; /* GC `granularity' */ + int gcstepmul; /* GC 'granularity' */ lua_CFunction panic; /* to be called in unprotected errors */ struct lua_State *mainthread; const lua_Number *version; /* pointer to version number */ TString *memerrmsg; /* memory-error message */ TString *tmname[TM_N]; /* array with tag-method names */ struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ + TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ } global_State; /* -** `per thread' state +** 'per thread' state */ struct lua_State { CommonHeader; + unsigned short nci; /* number of items in 'ci' list */ lu_byte status; StkId top; /* first free slot in the stack */ global_State *l_G; @@ -160,19 +157,20 @@ struct lua_State { const Instruction *oldpc; /* last pc traced */ StkId stack_last; /* last free slot in the stack */ StkId stack; /* stack base */ + UpVal *openupval; /* list of open upvalues in this stack */ + GCObject *gclist; + struct lua_State *twups; /* list of threads with open upvalues */ + struct lua_longjmp *errorJmp; /* current error recover point */ + CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ + lua_Hook hook; + ptrdiff_t errfunc; /* current error handling function (stack index) */ int stacksize; + int basehookcount; + int hookcount; unsigned short nny; /* number of non-yieldable calls in stack */ unsigned short nCcalls; /* number of nested C calls */ lu_byte hookmask; lu_byte allowhook; - int basehookcount; - int hookcount; - lua_Hook hook; - GCObject *openupval; /* list of open upvalues in this stack */ - GCObject *gclist; - struct lua_longjmp *errorJmp; /* current error recover point */ - ptrdiff_t errfunc; /* current error handling function (stack index) */ - CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ }; @@ -180,48 +178,47 @@ struct lua_State { /* -** Union of all collectable objects +** Union of all collectable objects (only for conversions) */ -union GCObject { - GCheader gch; /* common header */ - union TString ts; - union Udata u; +union GCUnion { + GCObject gc; /* common header */ + struct TString ts; + struct Udata u; union Closure cl; struct Table h; struct Proto p; - struct UpVal uv; struct lua_State th; /* thread */ }; -#define gch(o) (&(o)->gch) +#define cast_u(o) cast(union GCUnion *, (o)) /* macros to convert a GCObject into a specific value */ -#define rawgco2ts(o) \ - check_exp(novariant((o)->gch.tt) == LUA_TSTRING, &((o)->ts)) -#define gco2ts(o) (&rawgco2ts(o)->tsv) -#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) -#define gco2u(o) (&rawgco2u(o)->uv) -#define gco2lcl(o) check_exp((o)->gch.tt == LUA_TLCL, &((o)->cl.l)) -#define gco2ccl(o) check_exp((o)->gch.tt == LUA_TCCL, &((o)->cl.c)) +#define gco2ts(o) \ + check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) +#define gco2u(o) check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u)) +#define gco2lcl(o) check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l)) +#define gco2ccl(o) check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c)) #define gco2cl(o) \ - check_exp(novariant((o)->gch.tt) == LUA_TFUNCTION, &((o)->cl)) -#define gco2t(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) -#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) -#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) -#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) + check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) +#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h)) +#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p)) +#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th)) + -/* macro to convert any Lua object into a GCObject */ -#define obj2gco(v) (cast(GCObject *, (v))) +/* macro to convert a Lua object into a GCObject */ +#define obj2gco(v) \ + check_exp(novariant((v)->tt) < LUA_TDEADKEY, (&(cast_u(v)->gc))) /* actual number of total bytes allocated */ -#define gettotalbytes(g) ((g)->totalbytes + (g)->GCdebt) +#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L); +LUAI_FUNC void luaE_shrinkCI (lua_State *L); #endif diff --git a/deps/lua/lstring.c b/deps/lua/lstring.c index af96c89..9351766 100644 --- a/deps/lua/lstring.c +++ b/deps/lua/lstring.c @@ -1,23 +1,30 @@ /* -** $Id: lstring.c,v 2.26.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lstring.c,v 2.56 2015/11/23 11:32:51 roberto Exp $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ - -#include - #define lstring_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" +#include "ldebug.h" +#include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" +#define MEMERRMSG "not enough memory" + + /* ** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to ** compute its hash @@ -31,99 +38,126 @@ ** equality for long strings */ int luaS_eqlngstr (TString *a, TString *b) { - size_t len = a->tsv.len; - lua_assert(a->tsv.tt == LUA_TLNGSTR && b->tsv.tt == LUA_TLNGSTR); + size_t len = a->u.lnglen; + lua_assert(a->tt == LUA_TLNGSTR && b->tt == LUA_TLNGSTR); return (a == b) || /* same instance or... */ - ((len == b->tsv.len) && /* equal length and ... */ + ((len == b->u.lnglen) && /* equal length and ... */ (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ } -/* -** equality for strings -*/ -int luaS_eqstr (TString *a, TString *b) { - return (a->tsv.tt == b->tsv.tt) && - (a->tsv.tt == LUA_TSHRSTR ? eqshrstr(a, b) : luaS_eqlngstr(a, b)); -} - - unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { unsigned int h = seed ^ cast(unsigned int, l); - size_t l1; size_t step = (l >> LUAI_HASHLIMIT) + 1; - for (l1 = l; l1 >= step; l1 -= step) - h = h ^ ((h<<5) + (h>>2) + cast_byte(str[l1 - 1])); + for (; l >= step; l -= step) + h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); return h; } +unsigned int luaS_hashlongstr (TString *ts) { + lua_assert(ts->tt == LUA_TLNGSTR); + if (ts->extra == 0) { /* no hash? */ + ts->hash = luaS_hash(getstr(ts), ts->u.lnglen, ts->hash); + ts->extra = 1; /* now it has its hash */ + } + return ts->hash; +} + + /* ** resizes the string table */ void luaS_resize (lua_State *L, int newsize) { int i; stringtable *tb = &G(L)->strt; - /* cannot resize while GC is traversing strings */ - luaC_runtilstate(L, ~bitmask(GCSsweepstring)); - if (newsize > tb->size) { - luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); - for (i = tb->size; i < newsize; i++) tb->hash[i] = NULL; + if (newsize > tb->size) { /* grow table if needed */ + luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *); + for (i = tb->size; i < newsize; i++) + tb->hash[i] = NULL; } - /* rehash */ - for (i=0; isize; i++) { - GCObject *p = tb->hash[i]; + for (i = 0; i < tb->size; i++) { /* rehash */ + TString *p = tb->hash[i]; tb->hash[i] = NULL; while (p) { /* for each node in the list */ - GCObject *next = gch(p)->next; /* save next */ - unsigned int h = lmod(gco2ts(p)->hash, newsize); /* new position */ - gch(p)->next = tb->hash[h]; /* chain it */ + TString *hnext = p->u.hnext; /* save next */ + unsigned int h = lmod(p->hash, newsize); /* new position */ + p->u.hnext = tb->hash[h]; /* chain it */ tb->hash[h] = p; - resetoldbit(p); /* see MOVE OLD rule */ - p = next; + p = hnext; } } - if (newsize < tb->size) { - /* shrinking slice must be empty */ + if (newsize < tb->size) { /* shrink table if needed */ + /* vanishing slice should be empty */ lua_assert(tb->hash[newsize] == NULL && tb->hash[tb->size - 1] == NULL); - luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); + luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *); } tb->size = newsize; } +/* +** Clear API string cache. (Entries cannot be empty, so fill them with +** a non-collectable string.) +*/ +void luaS_clearcache (global_State *g) { + int i, j; + for (i = 0; i < STRCACHE_N; i++) + for (j = 0; j < STRCACHE_M; j++) { + if (iswhite(g->strcache[i][j])) /* will entry be collected? */ + g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ + } +} + + +/* +** Initialize the string table and the string cache +*/ +void luaS_init (lua_State *L) { + global_State *g = G(L); + int i, j; + luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + /* pre-create memory-error message */ + g->memerrmsg = luaS_newliteral(L, MEMERRMSG); + luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ + for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ + for (j = 0; j < STRCACHE_M; j++) + g->strcache[i][j] = g->memerrmsg; +} + + + /* ** creates a new string object */ -static TString *createstrobj (lua_State *L, const char *str, size_t l, - int tag, unsigned int h, GCObject **list) { +static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { TString *ts; + GCObject *o; size_t totalsize; /* total size of TString object */ - totalsize = sizeof(TString) + ((l + 1) * sizeof(char)); - ts = &luaC_newobj(L, tag, totalsize, list, 0)->ts; - ts->tsv.len = l; - ts->tsv.hash = h; - ts->tsv.extra = 0; - memcpy(ts+1, str, l*sizeof(char)); - ((char *)(ts+1))[l] = '\0'; /* ending 0 */ + totalsize = sizelstring(l); + o = luaC_newobj(L, tag, totalsize); + ts = gco2ts(o); + ts->hash = h; + ts->extra = 0; + getstr(ts)[l] = '\0'; /* ending 0 */ return ts; } -/* -** creates a new short string, inserting it into string table -*/ -static TString *newshrstr (lua_State *L, const char *str, size_t l, - unsigned int h) { - GCObject **list; /* (pointer to) list where it will be inserted */ +TString *luaS_createlngstrobj (lua_State *L, size_t l) { + TString *ts = createstrobj(L, l, LUA_TLNGSTR, G(L)->seed); + ts->u.lnglen = l; + return ts; +} + + +void luaS_remove (lua_State *L, TString *ts) { stringtable *tb = &G(L)->strt; - TString *s; - if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) - luaS_resize(L, tb->size*2); /* too crowded */ - list = &tb->hash[lmod(h, tb->size)]; - s = createstrobj(L, str, l, LUA_TSHRSTR, h, list); - tb->nuse++; - return s; + TString **p = &tb->hash[lmod(ts->hash, tb->size)]; + while (*p != ts) /* find previous element */ + p = &(*p)->u.hnext; + *p = (*p)->u.hnext; /* remove element from its list */ + tb->nuse--; } @@ -131,22 +165,31 @@ static TString *newshrstr (lua_State *L, const char *str, size_t l, ** checks whether short string exists and reuses it or creates a new one */ static TString *internshrstr (lua_State *L, const char *str, size_t l) { - GCObject *o; + TString *ts; global_State *g = G(L); unsigned int h = luaS_hash(str, l, g->seed); - for (o = g->strt.hash[lmod(h, g->strt.size)]; - o != NULL; - o = gch(o)->next) { - TString *ts = rawgco2ts(o); - if (h == ts->tsv.hash && - l == ts->tsv.len && + TString **list = &g->strt.hash[lmod(h, g->strt.size)]; + lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ + for (ts = *list; ts != NULL; ts = ts->u.hnext) { + if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { - if (isdead(G(L), o)) /* string is dead (but was not collected yet)? */ - changewhite(o); /* resurrect it */ + /* found! */ + if (isdead(g, ts)) /* dead (but not collected yet)? */ + changewhite(ts); /* resurrect it */ return ts; } } - return newshrstr(L, str, l, h); /* not found; create a new string */ + if (g->strt.nuse >= g->strt.size && g->strt.size <= MAX_INT/2) { + luaS_resize(L, g->strt.size * 2); + list = &g->strt.hash[lmod(h, g->strt.size)]; /* recompute with new size */ + } + ts = createstrobj(L, l, LUA_TSHRSTR, h); + memcpy(getstr(ts), str, l * sizeof(char)); + ts->shrlen = cast_byte(l); + ts->u.hnext = *list; + *list = ts; + g->strt.nuse++; + return ts; } @@ -157,29 +200,49 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { if (l <= LUAI_MAXSHORTLEN) /* short string? */ return internshrstr(L, str, l); else { - if (l + 1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) + TString *ts; + if (l >= (MAX_SIZE - sizeof(TString))/sizeof(char)) luaM_toobig(L); - return createstrobj(L, str, l, LUA_TLNGSTR, G(L)->seed, NULL); + ts = luaS_createlngstrobj(L, l); + memcpy(getstr(ts), str, l * sizeof(char)); + return ts; } } /* -** new zero-terminated string +** Create or reuse a zero-terminated string, first checking in the +** cache (using the string address as a key). The cache can contain +** only zero-terminated strings, so it is safe to use 'strcmp' to +** check hits. */ TString *luaS_new (lua_State *L, const char *str) { - return luaS_newlstr(L, str, strlen(str)); + unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ + int j; + TString **p = G(L)->strcache[i]; + for (j = 0; j < STRCACHE_M; j++) { + if (strcmp(str, getstr(p[j])) == 0) /* hit? */ + return p[j]; /* that is it */ + } + /* normal route */ + for (j = STRCACHE_M - 1; j > 0; j--) + p[j] = p[j - 1]; /* move out last element */ + /* new element is first in the list */ + p[0] = luaS_newlstr(L, str, strlen(str)); + return p[0]; } -Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { +Udata *luaS_newudata (lua_State *L, size_t s) { Udata *u; - if (s > MAX_SIZET - sizeof(Udata)) + GCObject *o; + if (s > MAX_SIZE - sizeof(Udata)) luaM_toobig(L); - u = &luaC_newobj(L, LUA_TUSERDATA, sizeof(Udata) + s, NULL, 0)->u; - u->uv.len = s; - u->uv.metatable = NULL; - u->uv.env = e; + o = luaC_newobj(L, LUA_TUSERDATA, sizeludata(s)); + u = gco2u(o); + u->len = s; + u->metatable = NULL; + setuservalue(L, u, luaO_nilobject); return u; } diff --git a/deps/lua/lstring.h b/deps/lua/lstring.h index 260e7f1..27efd20 100644 --- a/deps/lua/lstring.h +++ b/deps/lua/lstring.h @@ -1,5 +1,5 @@ /* -** $Id: lstring.h,v 1.49.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lstring.h,v 1.61 2015/11/03 15:36:01 roberto Exp $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -12,35 +12,38 @@ #include "lstate.h" -#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) +#define sizelstring(l) (sizeof(union UTString) + ((l) + 1) * sizeof(char)) -#define sizeudata(u) (sizeof(union Udata)+(u)->len) +#define sizeludata(l) (sizeof(union UUdata) + (l)) +#define sizeudata(u) sizeludata((u)->len) #define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ (sizeof(s)/sizeof(char))-1)) -#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) - /* ** test whether a string is a reserved word */ -#define isreserved(s) ((s)->tsv.tt == LUA_TSHRSTR && (s)->tsv.extra > 0) +#define isreserved(s) ((s)->tt == LUA_TSHRSTR && (s)->extra > 0) /* ** equality for short strings, which are always internalized */ -#define eqshrstr(a,b) check_exp((a)->tsv.tt == LUA_TSHRSTR, (a) == (b)) +#define eqshrstr(a,b) check_exp((a)->tt == LUA_TSHRSTR, (a) == (b)) LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); +LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); -LUAI_FUNC int luaS_eqstr (TString *a, TString *b); LUAI_FUNC void luaS_resize (lua_State *L, int newsize); -LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); +LUAI_FUNC void luaS_clearcache (global_State *g); +LUAI_FUNC void luaS_init (lua_State *L); +LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); +LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); #endif diff --git a/deps/lua/lstrlib.c b/deps/lua/lstrlib.c index 9261fd2..fe30e34 100644 --- a/deps/lua/lstrlib.c +++ b/deps/lua/lstrlib.c @@ -1,19 +1,23 @@ /* -** $Id: lstrlib.c,v 1.178.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lstrlib.c,v 1.239 2015/11/25 16:28:17 roberto Exp $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ +#define lstrlib_c +#define LUA_LIB + +#include "lprefix.h" + #include +#include +#include #include #include #include #include -#define lstrlib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -29,10 +33,21 @@ #endif -/* macro to `unsign' a character */ +/* macro to 'unsign' a character */ #define uchar(c) ((unsigned char)(c)) +/* +** Some sizes are better limited to fit in 'int', but must also fit in +** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) +*/ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +#define MAXSIZE \ + (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) + + + static int str_len (lua_State *L) { size_t l; @@ -43,22 +58,22 @@ static int str_len (lua_State *L) { /* translate a relative string position: negative means back from end */ -static size_t posrelat (ptrdiff_t pos, size_t len) { - if (pos >= 0) return (size_t)pos; +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 len - ((size_t)-pos) + 1; + else return (lua_Integer)len + pos + 1; } static int str_sub (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); - size_t start = posrelat(luaL_checkinteger(L, 2), l); - size_t end = posrelat(luaL_optinteger(L, 3, -1), l); + lua_Integer start = posrelat(luaL_checkinteger(L, 2), l); + lua_Integer end = posrelat(luaL_optinteger(L, 3, -1), l); if (start < 1) start = 1; - if (end > l) end = l; + if (end > (lua_Integer)l) end = l; if (start <= end) - lua_pushlstring(L, s + start - 1, end - start + 1); + lua_pushlstring(L, s + start - 1, (size_t)(end - start) + 1); else lua_pushliteral(L, ""); return 1; } @@ -102,25 +117,23 @@ static int str_upper (lua_State *L) { } -/* reasonable limit to avoid arithmetic overflow */ -#define MAXSIZE ((~(size_t)0) >> 1) - static int str_rep (lua_State *L) { size_t l, lsep; const char *s = luaL_checklstring(L, 1, &l); - int n = luaL_checkint(L, 2); + lua_Integer n = luaL_checkinteger(L, 2); const char *sep = luaL_optlstring(L, 3, "", &lsep); if (n <= 0) lua_pushliteral(L, ""); - else if (l + lsep < l || l + lsep >= MAXSIZE / n) /* may overflow? */ + else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */ return luaL_error(L, "resulting string too large"); else { - size_t totallen = n * l + (n - 1) * lsep; + size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; luaL_Buffer b; char *p = luaL_buffinitsize(L, &b, totallen); while (n-- > 1) { /* first n-1 copies (followed by separator) */ memcpy(p, s, l * sizeof(char)); p += l; - if (lsep > 0) { /* avoid empty 'memcpy' (may be expensive) */ - memcpy(p, sep, lsep * sizeof(char)); p += lsep; + if (lsep > 0) { /* empty 'memcpy' is not that cheap */ + memcpy(p, sep, lsep * sizeof(char)); + p += lsep; } } memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ @@ -133,15 +146,15 @@ static int str_rep (lua_State *L) { static int str_byte (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); - size_t posi = posrelat(luaL_optinteger(L, 2, 1), l); - size_t pose = posrelat(luaL_optinteger(L, 3, posi), l); + lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l); + lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l); int n, i; if (posi < 1) posi = 1; - if (pose > l) pose = l; + if (pose > (lua_Integer)l) pose = l; if (posi > pose) return 0; /* empty interval; return no values */ - n = (int)(pose - posi + 1); - if (posi + n <= pose) /* (size_t -> int) overflow? */ + if (pose - posi >= INT_MAX) /* arithmetic overflow? */ return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; luaL_checkstack(L, n, "string slice too long"); for (i=0; ip_end) - luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); + luaL_error(ms->L, "malformed pattern (ends with '%%')"); return p+1; } case '[': { if (*p == '^') p++; - do { /* look for a `]' */ + do { /* look for a ']' */ if (p == ms->p_end) - luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); + luaL_error(ms->L, "malformed pattern (missing ']')"); if (*(p++) == L_ESC && p < ms->p_end) - p++; /* skip escapes (e.g. `%]') */ + p++; /* skip escapes (e.g. '%]') */ } while (*p != ']'); return p+1; } @@ -287,7 +313,7 @@ static int matchbracketclass (int c, const char *p, const char *ec) { int sig = 1; if (*(p+1) == '^') { sig = 0; - p++; /* skip the `^' */ + p++; /* skip the '^' */ } while (++p < ec) { if (*p == L_ESC) { @@ -325,8 +351,7 @@ static int singlematch (MatchState *ms, const char *s, const char *p, static const char *matchbalance (MatchState *ms, const char *s, const char *p) { if (p >= ms->p_end - 1) - luaL_error(ms->L, "malformed pattern " - "(missing arguments to " LUA_QL("%%b") ")"); + luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); if (*s != *p) return NULL; else { int b = *p; @@ -425,7 +450,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) { break; } case '$': { - if ((p + 1) != ms->p_end) /* is the `$' the last char in pattern? */ + if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ goto dflt; /* no; go to default */ s = (s == ms->src_end) ? s : NULL; /* check end of string */ break; @@ -443,8 +468,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) { const char *ep; char previous; p += 2; if (*p != '[') - luaL_error(ms->L, "missing " LUA_QL("[") " after " - LUA_QL("%%f") " in pattern"); + luaL_error(ms->L, "missing '[' after '%%f' in pattern"); ep = classend(ms, p); /* points to what is next */ previous = (s == ms->src_init) ? '\0' : *(s - 1); if (!matchbracketclass(uchar(previous), p, ep - 1) && @@ -478,6 +502,8 @@ static const char *match (MatchState *ms, const char *s, const char *p) { s = NULL; /* fail */ } else { /* matched once */ + if (ms->nrep-- == 0) + luaL_error(ms->L, "pattern too complex"); switch (*ep) { /* handle optional suffix */ case '?': { /* optional */ const char *res; @@ -490,7 +516,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) { } case '+': /* 1 or more repetitions */ s++; /* 1 match already done */ - /* go through */ + /* FALLTHROUGH */ case '*': /* 0 or more repetitions */ s = max_expand(ms, s, p, ep); break; @@ -514,16 +540,16 @@ static const char *match (MatchState *ms, const char *s, const char *p) { static const char *lmemfind (const char *s1, size_t l1, const char *s2, size_t l2) { if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ else { - const char *init; /* to search for a `*s2' inside `s1' */ - l2--; /* 1st char will be checked by `memchr' */ - l1 = l1-l2; /* `s2' cannot be found after that */ + const char *init; /* to search for a '*s2' inside 's1' */ + l2--; /* 1st char will be checked by 'memchr' */ + l1 = l1-l2; /* 's2' cannot be found after that */ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { init++; /* 1st char is already checked */ if (memcmp(init, s2+1, l2) == 0) return init-1; - else { /* correct `l1' and `s1' to try again */ + else { /* correct 'l1' and 's1' to try again */ l1 -= init-s1; s1 = init; } @@ -539,13 +565,13 @@ static void push_onecapture (MatchState *ms, int i, const char *s, if (i == 0) /* ms->level == 0, too */ lua_pushlstring(ms->L, s, e - s); /* add whole match */ else - luaL_error(ms->L, "invalid capture index"); + luaL_error(ms->L, "invalid capture index %%%d", i + 1); } else { ptrdiff_t l = ms->capture[i].len; if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); if (l == CAP_POSITION) - lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); + lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); else lua_pushlstring(ms->L, ms->capture[i].init, l); } @@ -574,23 +600,43 @@ static int nospecials (const char *p, size_t l) { } +static void prepstate (MatchState *ms, lua_State *L, + const char *s, size_t ls, const char *p, size_t lp) { + ms->L = L; + ms->matchdepth = MAXCCALLS; + ms->src_init = s; + ms->src_end = s + ls; + ms->p_end = p + lp; + if (ls < (MAX_SIZET - B_REPS) / A_REPS) + ms->nrep = A_REPS * ls + B_REPS; + else /* overflow (very long subject) */ + ms->nrep = MAX_SIZET; /* no limit */ +} + + +static void reprepstate (MatchState *ms) { + ms->level = 0; + lua_assert(ms->matchdepth == MAXCCALLS); +} + + static int str_find_aux (lua_State *L, int find) { size_t ls, lp; const char *s = luaL_checklstring(L, 1, &ls); const char *p = luaL_checklstring(L, 2, &lp); - size_t init = posrelat(luaL_optinteger(L, 3, 1), ls); + lua_Integer init = posrelat(luaL_optinteger(L, 3, 1), ls); if (init < 1) init = 1; - else if (init > ls + 1) { /* start after string's end? */ + else if (init > (lua_Integer)ls + 1) { /* start after string's end? */ lua_pushnil(L); /* cannot find anything */ return 1; } /* explicit request or no special characters? */ if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { /* do a plain search */ - const char *s2 = lmemfind(s + init - 1, ls - init + 1, p, lp); + const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp); if (s2) { - lua_pushinteger(L, s2 - s + 1); - lua_pushinteger(L, s2 - s + lp); + lua_pushinteger(L, (s2 - s) + 1); + lua_pushinteger(L, (s2 - s) + lp); return 2; } } @@ -601,18 +647,13 @@ static int str_find_aux (lua_State *L, int find) { if (anchor) { p++; lp--; /* skip anchor character */ } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s + ls; - ms.p_end = p + lp; + prepstate(&ms, L, s, ls, p, lp); do { const char *res; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); + reprepstate(&ms); if ((res=match(&ms, s1, p)) != NULL) { if (find) { - lua_pushinteger(L, s1 - s + 1); /* start */ + lua_pushinteger(L, (s1 - s) + 1); /* start */ lua_pushinteger(L, res - s); /* end */ return push_captures(&ms, NULL, 0) + 2; } @@ -636,29 +677,26 @@ static int str_match (lua_State *L) { } +/* state for 'gmatch' */ +typedef struct GMatchState { + const char *src; /* current position */ + const char *p; /* pattern */ + MatchState ms; /* match state */ +} GMatchState; + + static int gmatch_aux (lua_State *L) { - MatchState ms; - size_t ls, lp; - const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); - const char *p = lua_tolstring(L, lua_upvalueindex(2), &lp); + GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); const char *src; - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s+ls; - ms.p_end = p + lp; - for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); - src <= ms.src_end; - src++) { + for (src = gm->src; src <= gm->ms.src_end; src++) { const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - if ((e = match(&ms, src, p)) != NULL) { - lua_Integer newstart = e-s; - if (e == src) newstart++; /* empty match? go at least one position */ - lua_pushinteger(L, newstart); - lua_replace(L, lua_upvalueindex(3)); - return push_captures(&ms, src, e); + reprepstate(&gm->ms); + if ((e = match(&gm->ms, src, gm->p)) != NULL) { + if (e == src) /* empty match? */ + gm->src =src + 1; /* go at least one position */ + else + gm->src = e; + return push_captures(&gm->ms, src, e); } } return 0; /* not found */ @@ -666,10 +704,14 @@ static int gmatch_aux (lua_State *L) { static int gmatch (lua_State *L) { - luaL_checkstring(L, 1); - luaL_checkstring(L, 2); - lua_settop(L, 2); - lua_pushinteger(L, 0); + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + GMatchState *gm; + lua_settop(L, 2); /* keep them on closure to avoid being collected */ + gm = (GMatchState *)lua_newuserdata(L, sizeof(GMatchState)); + prepstate(&gm->ms, L, s, ls, p, lp); + gm->src = s; gm->p = p; lua_pushcclosure(L, gmatch_aux, 3); return 1; } @@ -678,7 +720,8 @@ static int gmatch (lua_State *L) { static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, const char *e) { size_t l, i; - const char *news = lua_tolstring(ms->L, 3, &l); + lua_State *L = ms->L; + const char *news = lua_tolstring(L, 3, &l); for (i = 0; i < l; i++) { if (news[i] != L_ESC) luaL_addchar(b, news[i]); @@ -686,14 +729,15 @@ static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, i++; /* skip ESC */ if (!isdigit(uchar(news[i]))) { if (news[i] != L_ESC) - luaL_error(ms->L, "invalid use of " LUA_QL("%c") - " in replacement string", L_ESC); + luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); luaL_addchar(b, news[i]); } else if (news[i] == '0') luaL_addlstring(b, s, e - s); else { push_onecapture(ms, news[i] - '1', s, e); + luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ + lua_remove(L, -2); /* remove original value */ luaL_addvalue(b); /* add capture to accumulated result */ } } @@ -737,9 +781,9 @@ static int str_gsub (lua_State *L) { const char *src = luaL_checklstring(L, 1, &srcl); const char *p = luaL_checklstring(L, 2, &lp); int tr = lua_type(L, 3); - size_t max_s = luaL_optinteger(L, 4, srcl+1); + lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); int anchor = (*p == '^'); - size_t n = 0; + lua_Integer n = 0; MatchState ms; luaL_Buffer b; luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || @@ -749,17 +793,11 @@ static int str_gsub (lua_State *L) { if (anchor) { p++; lp--; /* skip anchor character */ } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = src; - ms.src_end = src+srcl; - ms.p_end = p + lp; + prepstate(&ms, L, src, srcl, p, lp); while (n < max_s) { const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - e = match(&ms, src, p); - if (e) { + reprepstate(&ms); + if ((e = match(&ms, src, p)) != NULL) { n++; add_value(&ms, &b, src, e, tr); } @@ -786,48 +824,102 @@ static int str_gsub (lua_State *L) { ** ======================================================= */ +#if !defined(lua_number2strx) /* { */ + /* -** LUA_INTFRMLEN is the length modifier for integer conversions in -** 'string.format'; LUA_INTFRM_T is the integer type corresponding to -** the previous length +** Hexadecimal floating-point formatter */ -#if !defined(LUA_INTFRMLEN) /* { */ -#if defined(LUA_USE_LONGLONG) -#define LUA_INTFRMLEN "ll" -#define LUA_INTFRM_T long long +#include +#include -#else +#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) -#define LUA_INTFRMLEN "l" -#define LUA_INTFRM_T long -#endif -#endif /* } */ +/* +** Number of bits that goes into the first digit. It can be any value +** between 1 and 4; the following definition tries to align the number +** to nibble boundaries by making what is left after that first digit a +** multiple of 4. +*/ +#define L_NBFD ((l_mathlim(MANT_DIG) - 1)%4 + 1) /* -** LUA_FLTFRMLEN is the length modifier for float conversions in -** 'string.format'; LUA_FLTFRM_T is the float type corresponding to -** the previous length +** Add integer part of 'x' to buffer and return new 'x' */ -#if !defined(LUA_FLTFRMLEN) +static lua_Number adddigit (char *buff, int n, lua_Number x) { + lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ + int d = (int)dd; + buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ + return x - dd; /* return what is left */ +} -#define LUA_FLTFRMLEN "" -#define LUA_FLTFRM_T double -#endif +static int num2straux (char *buff, int sz, lua_Number x) { + if (x != x || x == HUGE_VAL || x == -HUGE_VAL) /* inf or NaN? */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT, x); /* equal to '%g' */ + else if (x == 0) { /* can be -0... */ + /* create "0" or "-0" followed by exponent */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", x); + } + else { + int e; + lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ + int n = 0; /* character count */ + if (m < 0) { /* is number negative? */ + buff[n++] = '-'; /* add signal */ + m = -m; /* make it positive */ + } + buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */ + m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */ + e -= L_NBFD; /* this digit goes before the radix point */ + if (m > 0) { /* more digits? */ + buff[n++] = lua_getlocaledecpoint(); /* add radix point */ + do { /* add as many digits as needed */ + m = adddigit(buff, n++, m * 16); + } while (m > 0); + } + n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ + lua_assert(n < sz); + return n; + } +} + + +static int lua_number2strx (lua_State *L, char *buff, int sz, + const char *fmt, lua_Number x) { + int n = num2straux(buff, sz, x); + if (fmt[SIZELENMOD] == 'A') { + int i; + for (i = 0; i < n; i++) + buff[i] = toupper(uchar(buff[i])); + } + else if (fmt[SIZELENMOD] != 'a') + luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); + return n; +} + +#endif /* } */ + + +/* +** Maximum size of each formatted item. This maximum size is produced +** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', +** and '\0') + number of decimal digits to represent maxfloat (which +** is maximum exponent + 1). (99+3+1 then rounded to 120 for "extra +** expenses", such as locale-dependent stuff) +*/ +#define MAX_ITEM (120 + l_mathlim(MAX_10_EXP)) -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 /* valid flags in a format specification */ #define FLAGS "-+ #0" + /* -** maximum size of each format specification (such as '%-099.99d') -** (+10 accounts for %99.99x plus margin of error) +** maximum size of each format specification (such as "%-099.99d") */ -#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) +#define MAX_FORMAT 32 static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { @@ -842,9 +934,9 @@ static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { else if (*s == '\0' || iscntrl(uchar(*s))) { char buff[10]; if (!isdigit(uchar(*(s+1)))) - sprintf(buff, "\\%d", (int)uchar(*s)); + l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); else - sprintf(buff, "\\%03d", (int)uchar(*s)); + l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); luaL_addstring(b, buff); } else @@ -869,8 +961,8 @@ static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { if (isdigit(uchar(*p))) luaL_error(L, "invalid format (width or precision too long)"); *(form++) = '%'; - memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); - form += p - strfrmt + 1; + memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char)); + form += (p - strfrmt) + 1; *form = '\0'; return p; } @@ -903,7 +995,7 @@ static int str_format (lua_State *L) { else if (*++strfrmt == L_ESC) luaL_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ - char form[MAX_FORMAT]; /* to store the format (`%...') */ + char form[MAX_FORMAT]; /* to store the format ('%...') */ char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ int nb = 0; /* number of bytes in added item */ if (++arg > top) @@ -911,36 +1003,25 @@ static int str_format (lua_State *L) { strfrmt = scanformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { - nb = sprintf(buff, form, luaL_checkint(L, arg)); - break; - } - case 'd': case 'i': { - lua_Number n = luaL_checknumber(L, arg); - LUA_INTFRM_T ni = (LUA_INTFRM_T)n; - lua_Number diff = n - (lua_Number)ni; - luaL_argcheck(L, -1 < diff && diff < 1, arg, - "not a number in proper range"); - addlenmod(form, LUA_INTFRMLEN); - nb = sprintf(buff, form, ni); + nb = l_sprintf(buff, MAX_ITEM, form, (int)luaL_checkinteger(L, arg)); break; } + case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { - lua_Number n = luaL_checknumber(L, arg); - unsigned LUA_INTFRM_T ni = (unsigned LUA_INTFRM_T)n; - lua_Number diff = n - (lua_Number)ni; - luaL_argcheck(L, -1 < diff && diff < 1, arg, - "not a non-negative number in proper range"); - addlenmod(form, LUA_INTFRMLEN); - nb = sprintf(buff, form, ni); + lua_Integer n = luaL_checkinteger(L, arg); + addlenmod(form, LUA_INTEGER_FRMLEN); + nb = l_sprintf(buff, MAX_ITEM, form, n); break; } - case 'e': case 'E': case 'f': -#if defined(LUA_USE_AFORMAT) case 'a': case 'A': -#endif + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = lua_number2strx(L, buff, MAX_ITEM, form, + luaL_checknumber(L, arg)); + break; + case 'e': case 'E': case 'f': case 'g': case 'G': { - addlenmod(form, LUA_FLTFRMLEN); - nb = sprintf(buff, form, (LUA_FLTFRM_T)luaL_checknumber(L, arg)); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = l_sprintf(buff, MAX_ITEM, form, luaL_checknumber(L, arg)); break; } case 'q': { @@ -950,23 +1031,27 @@ static int str_format (lua_State *L) { case 's': { size_t l; const char *s = luaL_tolstring(L, arg, &l); - if (!strchr(form, '.') && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - luaL_addvalue(&b); - break; - } + if (form[2] == '\0') /* no modifiers? */ + luaL_addvalue(&b); /* keep entire string */ else { - nb = sprintf(buff, form, s); - lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ - break; + luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted */ + luaL_addvalue(&b); /* keep entire string */ + } + else { /* format the string into 'buff' */ + nb = l_sprintf(buff, MAX_ITEM, form, s); + lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + } } + break; } - default: { /* also treat cases `pnLlh' */ - return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " - LUA_QL("format"), *(strfrmt - 1)); + default: { /* also treat cases 'pnLlh' */ + return luaL_error(L, "invalid option '%%%c' to 'format'", + *(strfrmt - 1)); } } + lua_assert(nb < MAX_ITEM); luaL_addsize(&b, nb); } } @@ -977,6 +1062,452 @@ static int str_format (lua_State *L) { /* }====================================================== */ +/* +** {====================================================== +** PACK/UNPACK +** ======================================================= +*/ + + +/* value used for padding */ +#if !defined(LUA_PACKPADBYTE) +#define LUA_PACKPADBYTE 0x00 +#endif + +/* maximum size for the binary representation of an integer */ +#define MAXINTSIZE 16 + +/* number of bits in a character */ +#define NB CHAR_BIT + +/* mask for one character (NB 1's) */ +#define MC ((1 << NB) - 1) + +/* size of a lua_Integer */ +#define SZINT ((int)sizeof(lua_Integer)) + + +/* dummy union to get native endianness */ +static const union { + int dummy; + char little; /* true iff machine is little endian */ +} nativeendian = {1}; + + +/* dummy structure to get native alignment requirements */ +struct cD { + char c; + union { double d; void *p; lua_Integer i; lua_Number n; } u; +}; + +#define MAXALIGN (offsetof(struct cD, u)) + + +/* +** Union for serializing floats +*/ +typedef union Ftypes { + float f; + double d; + lua_Number n; + char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ +} Ftypes; + + +/* +** information to pack/unpack stuff +*/ +typedef struct Header { + lua_State *L; + int islittle; + int maxalign; +} Header; + + +/* +** options for pack/unpack +*/ +typedef enum KOption { + Kint, /* signed integers */ + Kuint, /* unsigned integers */ + Kfloat, /* floating-point numbers */ + Kchar, /* fixed-length strings */ + Kstring, /* strings with prefixed length */ + Kzstr, /* zero-terminated strings */ + Kpadding, /* padding */ + Kpaddalign, /* padding for alignment */ + Knop /* no-op (configuration or spaces) */ +} KOption; + + +/* +** Read an integer numeral from string 'fmt' or return 'df' if +** there is no numeral +*/ +static int digit (int c) { return '0' <= c && c <= '9'; } + +static int getnum (const char **fmt, int df) { + if (!digit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + int a = 0; + do { + a = a*10 + (*((*fmt)++) - '0'); + } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); + return a; + } +} + + +/* +** Read an integer numeral and raises an error if it is larger +** than the maximum size for integers. +*/ +static int getnumlimit (Header *h, const char **fmt, int df) { + int sz = getnum(fmt, df); + if (sz > MAXINTSIZE || sz <= 0) + luaL_error(h->L, "integral size (%d) out of limits [1,%d]", + sz, MAXINTSIZE); + return sz; +} + + +/* +** Initialize Header +*/ +static void initheader (lua_State *L, Header *h) { + h->L = L; + h->islittle = nativeendian.little; + h->maxalign = 1; +} + + +/* +** Read and classify next option. 'size' is filled with option's size. +*/ +static KOption getoption (Header *h, const char **fmt, int *size) { + int opt = *((*fmt)++); + *size = 0; /* default */ + switch (opt) { + case 'b': *size = sizeof(char); return Kint; + case 'B': *size = sizeof(char); return Kuint; + case 'h': *size = sizeof(short); return Kint; + case 'H': *size = sizeof(short); return Kuint; + case 'l': *size = sizeof(long); return Kint; + case 'L': *size = sizeof(long); return Kuint; + case 'j': *size = sizeof(lua_Integer); return Kint; + case 'J': *size = sizeof(lua_Integer); return Kuint; + case 'T': *size = sizeof(size_t); return Kuint; + case 'f': *size = sizeof(float); return Kfloat; + case 'd': *size = sizeof(double); return Kfloat; + case 'n': *size = sizeof(lua_Number); return Kfloat; + case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; + case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; + case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; + case 'c': + *size = getnum(fmt, -1); + if (*size == -1) + luaL_error(h->L, "missing size for format option 'c'"); + return Kchar; + case 'z': return Kzstr; + case 'x': *size = 1; return Kpadding; + case 'X': return Kpaddalign; + case ' ': break; + case '<': h->islittle = 1; break; + case '>': h->islittle = 0; break; + case '=': h->islittle = nativeendian.little; break; + case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; + default: luaL_error(h->L, "invalid format option '%c'", opt); + } + return Knop; +} + + +/* +** Read, classify, and fill other details about the next option. +** 'psize' is filled with option's size, 'notoalign' with its +** alignment requirements. +** Local variable 'size' gets the size to be aligned. (Kpadal option +** always gets its full alignment, other options are limited by +** the maximum alignment ('maxalign'). Kchar option needs no alignment +** despite its size. +*/ +static KOption getdetails (Header *h, size_t totalsize, + const char **fmt, int *psize, int *ntoalign) { + KOption opt = getoption(h, fmt, psize); + int align = *psize; /* usually, alignment follows size */ + if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ + if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) + luaL_argerror(h->L, 1, "invalid next option for option 'X'"); + } + if (align <= 1 || opt == Kchar) /* need no alignment? */ + *ntoalign = 0; + else { + if (align > h->maxalign) /* enforce maximum alignment */ + align = h->maxalign; + if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ + luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); + *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); + } + return opt; +} + + +/* +** Pack integer 'n' with 'size' bytes and 'islittle' endianness. +** The final 'if' handles the case when 'size' is larger than +** the size of a Lua integer, correcting the extra sign-extension +** bytes if necessary (by default they would be zeros). +*/ +static void packint (luaL_Buffer *b, lua_Unsigned n, + int islittle, int size, int neg) { + char *buff = luaL_prepbuffsize(b, size); + int i; + buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ + for (i = 1; i < size; i++) { + n >>= NB; + buff[islittle ? i : size - 1 - i] = (char)(n & MC); + } + if (neg && size > SZINT) { /* negative number need sign extension? */ + for (i = SZINT; i < size; i++) /* correct extra bytes */ + buff[islittle ? i : size - 1 - i] = (char)MC; + } + luaL_addsize(b, size); /* add result to buffer */ +} + + +/* +** Copy 'size' bytes from 'src' to 'dest', correcting endianness if +** given 'islittle' is different from native endianness. +*/ +static void copywithendian (volatile char *dest, volatile const char *src, + int size, int islittle) { + if (islittle == nativeendian.little) { + while (size-- != 0) + *(dest++) = *(src++); + } + else { + dest += size - 1; + while (size-- != 0) + *(dest--) = *(src++); + } +} + + +static int str_pack (lua_State *L) { + luaL_Buffer b; + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + int arg = 1; /* current argument to pack */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ + luaL_buffinit(L, &b); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + totalsize += ntoalign + size; + while (ntoalign-- > 0) + luaL_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */ + arg++; + switch (opt) { + case Kint: { /* signed integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) { /* need overflow check? */ + lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); + luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); + } + packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); + break; + } + case Kuint: { /* unsigned integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) /* need overflow check? */ + luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), + arg, "unsigned overflow"); + packint(&b, (lua_Unsigned)n, h.islittle, size, 0); + break; + } + case Kfloat: { /* floating-point options */ + volatile Ftypes u; + char *buff = luaL_prepbuffsize(&b, size); + lua_Number n = luaL_checknumber(L, arg); /* get argument */ + if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ + else if (size == sizeof(u.d)) u.d = (double)n; + else u.n = n; + /* move 'u' to final result, correcting endianness if needed */ + copywithendian(buff, u.buff, size, h.islittle); + luaL_addsize(&b, size); + break; + } + case Kchar: { /* fixed-size string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + if ((size_t)size <= len) /* string larger than (or equal to) needed? */ + luaL_addlstring(&b, s, size); /* truncate string to asked size */ + else { /* string smaller than needed */ + luaL_addlstring(&b, s, len); /* add it all */ + while (len++ < (size_t)size) /* pad extra space */ + luaL_addchar(&b, LUA_PACKPADBYTE); + } + break; + } + case Kstring: { /* strings with length count */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, size >= (int)sizeof(size_t) || + len < ((size_t)1 << (size * NB)), + arg, "string length does not fit in given size"); + packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ + luaL_addlstring(&b, s, len); + totalsize += len; + break; + } + case Kzstr: { /* zero-terminated string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); + luaL_addlstring(&b, s, len); + luaL_addchar(&b, '\0'); /* add zero at the end */ + totalsize += len + 1; + break; + } + case Kpadding: luaL_addchar(&b, LUA_PACKPADBYTE); /* FALLTHROUGH */ + case Kpaddalign: case Knop: + arg--; /* undo increment */ + break; + } + } + luaL_pushresult(&b); + return 1; +} + + +static int str_packsize (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + size += ntoalign; /* total space used by option */ + luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, + "format result too large"); + totalsize += size; + switch (opt) { + case Kstring: /* strings with length count */ + case Kzstr: /* zero-terminated string */ + luaL_argerror(L, 1, "variable-length format"); + /* call never return, but to avoid warnings: *//* FALLTHROUGH */ + default: break; + } + } + lua_pushinteger(L, (lua_Integer)totalsize); + return 1; +} + + +/* +** Unpack an integer with 'size' bytes and 'islittle' endianness. +** If size is smaller than the size of a Lua integer and integer +** is signed, must do sign extension (propagating the sign to the +** higher bits); if size is larger than the size of a Lua integer, +** it must check the unread bytes to see whether they do not cause an +** overflow. +*/ +static lua_Integer unpackint (lua_State *L, const char *str, + int islittle, int size, int issigned) { + lua_Unsigned res = 0; + int i; + int limit = (size <= SZINT) ? size : SZINT; + for (i = limit - 1; i >= 0; i--) { + res <<= NB; + res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + } + if (size < SZINT) { /* real size smaller than lua_Integer? */ + if (issigned) { /* needs sign extension? */ + lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); + res = ((res ^ mask) - mask); /* do sign extension */ + } + } + else if (size > SZINT) { /* must check unread bytes */ + int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; + for (i = limit; i < size; i++) { + if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) + luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); + } + } + return (lua_Integer)res; +} + + +static int str_unpack (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); + size_t ld; + const char *data = luaL_checklstring(L, 2, &ld); + size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1; + int n = 0; /* number of results */ + luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); + if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld) + luaL_argerror(L, 2, "data string too short"); + pos += ntoalign; /* skip alignment */ + /* stack space for item + next position */ + luaL_checkstack(L, 2, "too many results"); + n++; + switch (opt) { + case Kint: + case Kuint: { + lua_Integer res = unpackint(L, data + pos, h.islittle, size, + (opt == Kint)); + lua_pushinteger(L, res); + break; + } + case Kfloat: { + volatile Ftypes u; + lua_Number num; + copywithendian(u.buff, data + pos, size, h.islittle); + if (size == sizeof(u.f)) num = (lua_Number)u.f; + else if (size == sizeof(u.d)) num = (lua_Number)u.d; + else num = u.n; + lua_pushnumber(L, num); + break; + } + case Kchar: { + lua_pushlstring(L, data + pos, size); + break; + } + case Kstring: { + size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); + luaL_argcheck(L, pos + len + size <= ld, 2, "data string too short"); + lua_pushlstring(L, data + pos + size, len); + pos += len; /* skip string */ + break; + } + case Kzstr: { + size_t len = (int)strlen(data + pos); + lua_pushlstring(L, data + pos, len); + pos += len + 1; /* skip string plus final '\0' */ + break; + } + case Kpaddalign: case Kpadding: case Knop: + n--; /* undo increment */ + break; + } + pos += size; + } + lua_pushinteger(L, pos + 1); /* next position */ + return n + 1; +} + +/* }====================================================== */ + + static const luaL_Reg strlib[] = { {"byte", str_byte}, {"char", str_char}, @@ -992,6 +1523,9 @@ static const luaL_Reg strlib[] = { {"reverse", str_reverse}, {"sub", str_sub}, {"upper", str_upper}, + {"pack", str_pack}, + {"packsize", str_packsize}, + {"unpack", str_unpack}, {NULL, NULL} }; diff --git a/deps/lua/ltable.c b/deps/lua/ltable.c index 5d76f97..7e15b71 100644 --- a/deps/lua/ltable.c +++ b/deps/lua/ltable.c @@ -1,27 +1,30 @@ /* -** $Id: ltable.c,v 2.72.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ltable.c,v 2.117 2015/11/19 19:16:22 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ +#define ltable_c +#define LUA_CORE + +#include "lprefix.h" + /* ** Implementation of tables (aka arrays, objects, or hash tables). ** Tables keep its elements in two parts: an array part and a hash part. ** Non-negative integer keys are all candidates to be kept in the array -** part. The actual size of the array is the largest `n' such that at -** least half the slots between 0 and n are in use. +** part. The actual size of the array is the largest 'n' such that +** more than half the slots between 1 and n are in use. ** Hash uses a mix of chained scatter table with Brent's variation. ** A main invariant of these tables is that, if an element is not -** in its main position (i.e. the `original' position that its hash gives +** in its main position (i.e. the 'original' position that its hash gives ** to it), then the colliding element is in its own main position. ** Hence even when the load factor reaches 100%, performance remains good. */ -#include - -#define ltable_c -#define LUA_CORE +#include +#include #include "lua.h" @@ -37,21 +40,26 @@ /* -** max size of array part is 2^MAXBITS +** Maximum size of array part (MAXASIZE) is 2^MAXABITS. MAXABITS is +** the largest integer such that MAXASIZE fits in an unsigned int. */ -#if LUAI_BITSINT >= 32 -#define MAXBITS 30 -#else -#define MAXBITS (LUAI_BITSINT-2) -#endif +#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) +#define MAXASIZE (1u << MAXABITS) -#define MAXASIZE (1 << MAXBITS) +/* +** Maximum size of hash part is 2^MAXHBITS. MAXHBITS is the largest +** integer such that 2^MAXHBITS fits in a signed int. (Note that the +** maximum number of elements in a table, 2^MAXABITS + 2^MAXHBITS, still +** fits comfortably in an unsigned int.) +*/ +#define MAXHBITS (MAXABITS - 1) #define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) -#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) +#define hashstr(t,str) hashpow2(t, (str)->hash) #define hashboolean(t,p) hashpow2(t, p) +#define hashint(t,i) hashpow2(t, i) /* @@ -61,7 +69,7 @@ #define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) -#define hashpointer(t,p) hashmod(t, IntPoint(p)) +#define hashpointer(t,p) hashmod(t, point2uint(p)) #define dummynode (&dummynode_) @@ -70,44 +78,54 @@ static const Node dummynode_ = { {NILCONSTANT}, /* value */ - {{NILCONSTANT, NULL}} /* key */ + {{NILCONSTANT, 0}} /* key */ }; /* -** hash for lua_Numbers +** Hash for floating-point numbers. +** The main computation should be just +** n = frexp(n, &i); return (n * INT_MAX) + i +** but there are some numerical subtleties. +** In a two-complement representation, INT_MAX does not has an exact +** representation as a float, but INT_MIN does; because the absolute +** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the +** absolute value of the product 'frexp * -INT_MIN' is smaller or equal +** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when +** adding 'i'; the use of '~u' (instead of '-u') avoids problems with +** INT_MIN. */ -static Node *hashnum (const Table *t, lua_Number n) { +#if !defined(l_hashfloat) +static int l_hashfloat (lua_Number n) { int i; - luai_hashnum(i, n); - if (i < 0) { - if (cast(unsigned int, i) == 0u - i) /* use unsigned to avoid overflows */ - i = 0; /* handle INT_MIN */ - i = -i; /* must be a positive value */ + lua_Integer ni; + n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); + if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ + lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); + return 0; + } + else { /* normal case */ + unsigned int u = cast(unsigned int, i) + cast(unsigned int, ni); + return cast_int(u <= cast(unsigned int, INT_MAX) ? u : ~u); } - return hashmod(t, i); } - +#endif /* -** returns the `main' position of an element in a table (that is, the index +** returns the 'main' position of an element in a table (that is, the index ** of its hash value) */ static Node *mainposition (const Table *t, const TValue *key) { switch (ttype(key)) { - case LUA_TNUMBER: - return hashnum(t, nvalue(key)); - case LUA_TLNGSTR: { - TString *s = rawtsvalue(key); - if (s->tsv.extra == 0) { /* no hash? */ - s->tsv.hash = luaS_hash(getstr(s), s->tsv.len, s->tsv.hash); - s->tsv.extra = 1; /* now it has its hash */ - } - return hashstr(t, rawtsvalue(key)); - } + case LUA_TNUMINT: + return hashint(t, ivalue(key)); + case LUA_TNUMFLT: + return hashmod(t, l_hashfloat(fltvalue(key))); case LUA_TSHRSTR: - return hashstr(t, rawtsvalue(key)); + return hashstr(t, tsvalue(key)); + case LUA_TLNGSTR: + return hashpow2(t, luaS_hashlongstr(tsvalue(key))); case LUA_TBOOLEAN: return hashboolean(t, bvalue(key)); case LUA_TLIGHTUSERDATA: @@ -115,67 +133,68 @@ static Node *mainposition (const Table *t, const TValue *key) { case LUA_TLCF: return hashpointer(t, fvalue(key)); default: + lua_assert(!ttisdeadkey(key)); return hashpointer(t, gcvalue(key)); } } /* -** returns the index for `key' if `key' is an appropriate key to live in -** the array part of the table, -1 otherwise. +** returns the index for 'key' if 'key' is an appropriate key to live in +** the array part of the table, 0 otherwise. */ -static int arrayindex (const TValue *key) { - if (ttisnumber(key)) { - lua_Number n = nvalue(key); - int k; - lua_number2int(k, n); - if (luai_numeq(cast_num(k), n)) - return k; +static unsigned int arrayindex (const TValue *key) { + if (ttisinteger(key)) { + lua_Integer k = ivalue(key); + if (0 < k && (lua_Unsigned)k <= MAXASIZE) + return cast(unsigned int, k); /* 'key' is an appropriate array index */ } - return -1; /* `key' did not match some condition */ + return 0; /* 'key' did not match some condition */ } /* -** returns the index of a `key' for table traversals. First goes all +** returns the index of a 'key' for table traversals. First goes all ** elements in the array part, then elements in the hash part. The -** beginning of a traversal is signaled by -1. +** beginning of a traversal is signaled by 0. */ -static int findindex (lua_State *L, Table *t, StkId key) { - int i; - if (ttisnil(key)) return -1; /* first iteration */ +static unsigned int findindex (lua_State *L, Table *t, StkId key) { + unsigned int i; + if (ttisnil(key)) return 0; /* first iteration */ i = arrayindex(key); - if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ - return i-1; /* yes; that's the index (corrected to C) */ + if (i != 0 && i <= t->sizearray) /* is 'key' inside array part? */ + return i; /* yes; that's the index */ else { + int nx; Node *n = mainposition(t, key); - for (;;) { /* check whether `key' is somewhere in the chain */ - /* key may be dead already, but it is ok to use it in `next' */ + for (;;) { /* check whether 'key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in 'next' */ if (luaV_rawequalobj(gkey(n), key) || (ttisdeadkey(gkey(n)) && iscollectable(key) && deadvalue(gkey(n)) == gcvalue(key))) { i = cast_int(n - gnode(t, 0)); /* key index in hash table */ /* hash elements are numbered after array ones */ - return i + t->sizearray; + return (i + 1) + t->sizearray; } - else n = gnext(n); - if (n == NULL) - luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + nx = gnext(n); + if (nx == 0) + luaG_runerror(L, "invalid key to 'next'"); /* key not found */ + else n += nx; } } } int luaH_next (lua_State *L, Table *t, StkId key) { - int i = findindex(L, t, key); /* find original element */ - for (i++; i < t->sizearray; i++) { /* try first array part */ + unsigned int i = findindex(L, t, key); /* find original element */ + for (; i < t->sizearray; i++) { /* try first array part */ if (!ttisnil(&t->array[i])) { /* a non-nil value? */ - setnvalue(key, cast_num(i+1)); + setivalue(key, i + 1); setobj2s(L, key+1, &t->array[i]); return 1; } } - for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ + for (i -= t->sizearray; cast_int(i) < sizenode(t); i++) { /* hash part */ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ setobj2s(L, key, gkey(gnode(t, i))); setobj2s(L, key+1, gval(gnode(t, i))); @@ -192,32 +211,38 @@ int luaH_next (lua_State *L, Table *t, StkId key) { ** ============================================================== */ - -static int computesizes (int nums[], int *narray) { +/* +** Compute the optimal size for the array part of table 't'. 'nums' is a +** "count array" where 'nums[i]' is the number of integers in the table +** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of +** integer keys in the table and leaves with the number of keys that +** will go to the array part; return the optimal size. +*/ +static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { int i; - int twotoi; /* 2^i */ - int a = 0; /* number of elements smaller than 2^i */ - int na = 0; /* number of elements to go to array part */ - int n = 0; /* optimal size for array part */ - for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { + unsigned int twotoi; /* 2^i (candidate for optimal size) */ + unsigned int a = 0; /* number of elements smaller than 2^i */ + unsigned int na = 0; /* number of elements to go to array part */ + unsigned int optimal = 0; /* optimal size for array part */ + /* loop while keys can fill more than half of total size */ + for (i = 0, twotoi = 1; *pna > twotoi / 2; i++, twotoi *= 2) { if (nums[i] > 0) { a += nums[i]; if (a > twotoi/2) { /* more than half elements present? */ - n = twotoi; /* optimal size (till now) */ - na = a; /* all elements smaller than n will go to array part */ + optimal = twotoi; /* optimal size (till now) */ + na = a; /* all elements up to 'optimal' will go to array part */ } } - if (a == *narray) break; /* all elements already counted */ } - *narray = n; - lua_assert(*narray/2 <= na && na <= *narray); - return na; + lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); + *pna = na; + return optimal; } -static int countint (const TValue *key, int *nums) { - int k = arrayindex(key); - if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ +static int countint (const TValue *key, unsigned int *nums) { + unsigned int k = arrayindex(key); + if (k != 0) { /* is 'key' an appropriate array index? */ nums[luaO_ceillog2(k)]++; /* count as such */ return 1; } @@ -226,20 +251,26 @@ static int countint (const TValue *key, int *nums) { } -static int numusearray (const Table *t, int *nums) { +/* +** Count keys in array part of table 't': Fill 'nums[i]' with +** number of keys that will go into corresponding slice and return +** total number of non-nil keys. +*/ +static unsigned int numusearray (const Table *t, unsigned int *nums) { int lg; - int ttlg; /* 2^lg */ - int ause = 0; /* summation of `nums' */ - int i = 1; /* count to traverse all array keys */ - for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ - int lc = 0; /* counter */ - int lim = ttlg; + unsigned int ttlg; /* 2^lg */ + unsigned int ause = 0; /* summation of 'nums' */ + unsigned int i = 1; /* count to traverse all array keys */ + /* traverse each slice */ + for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { + unsigned int lc = 0; /* counter */ + unsigned int lim = ttlg; if (lim > t->sizearray) { lim = t->sizearray; /* adjust upper limit */ if (i > lim) break; /* no more elements to count */ } - /* count elements in range (2^(lg-1), 2^lg] */ + /* count elements in range (2^(lg - 1), 2^lg] */ for (; i <= lim; i++) { if (!ttisnil(&t->array[i-1])) lc++; @@ -251,9 +282,9 @@ static int numusearray (const Table *t, int *nums) { } -static int numusehash (const Table *t, int *nums, int *pnasize) { +static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { int totaluse = 0; /* total number of elements */ - int ause = 0; /* summation of `nums' */ + int ause = 0; /* elements added to 'nums' (can go to array part) */ int i = sizenode(t); while (i--) { Node *n = &t->node[i]; @@ -262,13 +293,13 @@ static int numusehash (const Table *t, int *nums, int *pnasize) { totaluse++; } } - *pnasize += ause; + *pna += ause; return totaluse; } -static void setarrayvector (lua_State *L, Table *t, int size) { - int i; +static void setarrayvector (lua_State *L, Table *t, unsigned int size) { + unsigned int i; luaM_reallocvector(L, t->array, t->sizearray, size, TValue); for (i=t->sizearray; iarray[i]); @@ -276,23 +307,23 @@ static void setarrayvector (lua_State *L, Table *t, int size) { } -static void setnodevector (lua_State *L, Table *t, int size) { +static void setnodevector (lua_State *L, Table *t, unsigned int size) { int lsize; if (size == 0) { /* no elements to hash part? */ - t->node = cast(Node *, dummynode); /* use common `dummynode' */ + t->node = cast(Node *, dummynode); /* use common 'dummynode' */ lsize = 0; } else { int i; lsize = luaO_ceillog2(size); - if (lsize > MAXBITS) + if (lsize > MAXHBITS) luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); - for (i=0; isizearray; +void luaH_resize (lua_State *L, Table *t, unsigned int nasize, + unsigned int nhsize) { + unsigned int i; + int j; + unsigned int oldasize = t->sizearray; int oldhsize = t->lsizenode; Node *nold = t->node; /* save old hash ... */ if (nasize > oldasize) /* array part must grow? */ @@ -321,8 +354,8 @@ void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize) { luaM_reallocvector(L, t->array, oldasize, nasize, TValue); } /* re-insert elements from hash part */ - for (i = twoto(oldhsize) - 1; i >= 0; i--) { - Node *old = nold+i; + for (j = twoto(oldhsize) - 1; j >= 0; j--) { + Node *old = nold + j; if (!ttisnil(gval(old))) { /* doesn't need barrier/invalidate cache, as entry was already present in the table */ @@ -330,32 +363,35 @@ void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize) { } } if (!isdummy(nold)) - luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old array */ + luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old hash */ } -void luaH_resizearray (lua_State *L, Table *t, int nasize) { +void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) { int nsize = isdummy(t->node) ? 0 : sizenode(t); luaH_resize(L, t, nasize, nsize); } - +/* +** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i +*/ static void rehash (lua_State *L, Table *t, const TValue *ek) { - int nasize, na; - int nums[MAXBITS+1]; /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */ + unsigned int asize; /* optimal size for array part */ + unsigned int na; /* number of keys in the array part */ + unsigned int nums[MAXABITS + 1]; int i; int totaluse; - for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ - nasize = numusearray(t, nums); /* count keys in array part */ - totaluse = nasize; /* all those keys are integer keys */ - totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */ + na = numusearray(t, nums); /* count keys in array part */ + totaluse = na; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &na); /* count keys in hash part */ /* count extra key */ - nasize += countint(ek, nums); + na += countint(ek, nums); totaluse++; /* compute new size for array part */ - na = computesizes(nums, &nasize); + asize = computesizes(nums, &na); /* resize the table to new computed sizes */ - luaH_resize(L, t, nasize, totaluse - na); + luaH_resize(L, t, asize, totaluse - na); } @@ -366,7 +402,8 @@ static void rehash (lua_State *L, Table *t, const TValue *ek) { Table *luaH_new (lua_State *L) { - Table *t = &luaC_newobj(L, LUA_TTABLE, sizeof(Table), NULL, 0)->h; + GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table)); + Table *t = gco2t(o); t->metatable = NULL; t->flags = cast_byte(~0); t->array = NULL; @@ -404,37 +441,51 @@ static Node *getfreepos (Table *t) { */ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { Node *mp; + TValue aux; if (ttisnil(key)) luaG_runerror(L, "table index is nil"); - else if (ttisnumber(key) && luai_numisnan(L, nvalue(key))) - luaG_runerror(L, "table index is NaN"); + else if (ttisfloat(key)) { + lua_Integer k; + if (luaV_tointeger(key, &k, 0)) { /* index is int? */ + setivalue(&aux, k); + key = &aux; /* insert it as an integer */ + } + else if (luai_numisnan(fltvalue(key))) + luaG_runerror(L, "table index is NaN"); + } mp = mainposition(t, key); if (!ttisnil(gval(mp)) || isdummy(mp)) { /* main position is taken? */ Node *othern; - Node *n = getfreepos(t); /* get a free place */ - if (n == NULL) { /* cannot find a free place? */ + Node *f = getfreepos(t); /* get a free place */ + if (f == NULL) { /* cannot find a free place? */ rehash(L, t, key); /* grow table */ - /* whatever called 'newkey' take care of TM cache and GC barrier */ + /* whatever called 'newkey' takes care of TM cache */ return luaH_set(L, t, key); /* insert key into grown table */ } - lua_assert(!isdummy(n)); + lua_assert(!isdummy(f)); othern = mainposition(t, gkey(mp)); if (othern != mp) { /* is colliding node out of its main position? */ /* yes; move colliding node into free position */ - while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ - gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ - *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ - gnext(mp) = NULL; /* now `mp' is free */ + while (othern + gnext(othern) != mp) /* find previous */ + othern += gnext(othern); + gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */ + *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + if (gnext(mp) != 0) { + gnext(f) += cast_int(mp - f); /* correct 'next' */ + gnext(mp) = 0; /* now 'mp' is free */ + } setnilvalue(gval(mp)); } else { /* colliding node is in its own main position */ /* new node will go into free position */ - gnext(n) = gnext(mp); /* chain new position */ - gnext(mp) = n; - mp = n; + if (gnext(mp) != 0) + gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */ + else lua_assert(gnext(f) == 0); + gnext(mp) = cast_int(f - mp); + mp = f; } } - setobj2t(L, gkey(mp), key); - luaC_barrierback(L, obj2gco(t), key); + setnodekey(L, &mp->i_key, key); + luaC_barrierback(L, t, key); lua_assert(ttisnil(gval(mp))); return gval(mp); } @@ -443,18 +494,21 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { /* ** search function for integers */ -const TValue *luaH_getint (Table *t, int key) { +const TValue *luaH_getint (Table *t, lua_Integer key) { /* (1 <= key && key <= t->sizearray) */ - if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) - return &t->array[key-1]; + if (l_castS2U(key) - 1 < t->sizearray) + return &t->array[key - 1]; else { - lua_Number nk = cast_num(key); - Node *n = hashnum(t, nk); - do { /* check whether `key' is somewhere in the chain */ - if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) + Node *n = hashint(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (ttisinteger(gkey(n)) && ivalue(gkey(n)) == key) return gval(n); /* that's it */ - else n = gnext(n); - } while (n); + else { + int nx = gnext(n); + if (nx == 0) break; + n += nx; + } + } return luaO_nilobject; } } @@ -463,15 +517,50 @@ const TValue *luaH_getint (Table *t, int key) { /* ** search function for short strings */ -const TValue *luaH_getstr (Table *t, TString *key) { +const TValue *luaH_getshortstr (Table *t, TString *key) { Node *n = hashstr(t, key); - lua_assert(key->tsv.tt == LUA_TSHRSTR); - do { /* check whether `key' is somewhere in the chain */ - if (ttisshrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)), key)) + lua_assert(key->tt == LUA_TSHRSTR); + for (;;) { /* check whether 'key' is somewhere in the chain */ + const TValue *k = gkey(n); + if (ttisshrstring(k) && eqshrstr(tsvalue(k), key)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return luaO_nilobject; /* not found */ + n += nx; + } + } +} + + +/* +** "Generic" get version. (Not that generic: not valid for integers, +** which may be in array part, nor for floats with integral values.) +*/ +static const TValue *getgeneric (Table *t, const TValue *key) { + Node *n = mainposition(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (luaV_rawequalobj(gkey(n), key)) return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; + else { + int nx = gnext(n); + if (nx == 0) + return luaO_nilobject; /* not found */ + n += nx; + } + } +} + + +const TValue *luaH_getstr (Table *t, TString *key) { + if (key->tt == LUA_TSHRSTR) + return luaH_getshortstr(t, key); + else { /* for long strings, use generic case */ + TValue ko; + setsvalue(cast(lua_State *, NULL), &ko, key); + return getgeneric(t, &ko); + } } @@ -480,25 +569,17 @@ const TValue *luaH_getstr (Table *t, TString *key) { */ const TValue *luaH_get (Table *t, const TValue *key) { switch (ttype(key)) { - case LUA_TSHRSTR: return luaH_getstr(t, rawtsvalue(key)); + case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key)); + case LUA_TNUMINT: return luaH_getint(t, ivalue(key)); case LUA_TNIL: return luaO_nilobject; - case LUA_TNUMBER: { - int k; - lua_Number n = nvalue(key); - lua_number2int(k, n); - if (luai_numeq(cast_num(k), n)) /* index is int? */ + case LUA_TNUMFLT: { + lua_Integer k; + if (luaV_tointeger(key, &k, 0)) /* index is int? */ return luaH_getint(t, k); /* use specialized version */ - /* else go through */ - } - default: { - Node *n = mainposition(t, key); - do { /* check whether `key' is somewhere in the chain */ - if (luaV_rawequalobj(gkey(n), key)) - return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; - } + /* else... */ + } /* FALLTHROUGH */ + default: + return getgeneric(t, key); } } @@ -515,14 +596,14 @@ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { } -void luaH_setint (lua_State *L, Table *t, int key, TValue *value) { +void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { const TValue *p = luaH_getint(t, key); TValue *cell; if (p != luaO_nilobject) cell = cast(TValue *, p); else { TValue k; - setnvalue(&k, cast_num(key)); + setivalue(&k, key); cell = luaH_newkey(L, t, &k); } setobj2t(L, cell, value); @@ -532,16 +613,16 @@ void luaH_setint (lua_State *L, Table *t, int key, TValue *value) { static int unbound_search (Table *t, unsigned int j) { unsigned int i = j; /* i is zero or a present index */ j++; - /* find `i' and `j' such that i is present and j is not */ + /* find 'i' and 'j' such that i is present and j is not */ while (!ttisnil(luaH_getint(t, j))) { i = j; - j *= 2; - if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ + if (j > cast(unsigned int, MAX_INT)/2) { /* overflow? */ /* table was built with bad purposes: resort to linear search */ i = 1; while (!ttisnil(luaH_getint(t, i))) i++; return i - 1; } + j *= 2; } /* now do a binary search between them */ while (j - i > 1) { @@ -554,7 +635,7 @@ static int unbound_search (Table *t, unsigned int j) { /* -** Try to find a boundary in table `t'. A `boundary' is an integer index +** Try to find a boundary in table 't'. A 'boundary' is an integer index ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). */ int luaH_getn (Table *t) { diff --git a/deps/lua/ltable.h b/deps/lua/ltable.h index d69449b..213cc13 100644 --- a/deps/lua/ltable.h +++ b/deps/lua/ltable.h @@ -1,5 +1,5 @@ /* -** $Id: ltable.h,v 2.16.1.2 2013/08/30 15:49:41 roberto Exp $ +** $Id: ltable.h,v 2.21 2015/11/03 15:47:30 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ @@ -11,26 +11,39 @@ #define gnode(t,i) (&(t)->node[i]) -#define gkey(n) (&(n)->i_key.tvk) #define gval(n) (&(n)->i_val) #define gnext(n) ((n)->i_key.nk.next) + +/* 'const' to avoid wrong writings that can mess up field 'next' */ +#define gkey(n) cast(const TValue*, (&(n)->i_key.tvk)) + +/* +** writable version of 'gkey'; allows updates to individual fields, +** but not to the whole (which has incompatible type) +*/ +#define wgkey(n) (&(n)->i_key.nk) + #define invalidateTMcache(t) ((t)->flags = 0) + /* returns the key, given the value of a table entry */ #define keyfromval(v) \ (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val)))) -LUAI_FUNC const TValue *luaH_getint (Table *t, int key); -LUAI_FUNC void luaH_setint (lua_State *L, Table *t, int key, TValue *value); +LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); +LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, + TValue *value); +LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key); LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); LUAI_FUNC Table *luaH_new (lua_State *L); -LUAI_FUNC void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize); -LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); +LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, + unsigned int nhsize); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); LUAI_FUNC void luaH_free (lua_State *L, Table *t); LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); LUAI_FUNC int luaH_getn (Table *t); diff --git a/deps/lua/ltablib.c b/deps/lua/ltablib.c index 6001224..b3c9a7c 100644 --- a/deps/lua/ltablib.c +++ b/deps/lua/ltablib.c @@ -1,23 +1,61 @@ /* -** $Id: ltablib.c,v 1.65.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ltablib.c,v 1.90 2015/11/25 12:48:57 roberto Exp $ ** Library for Table Manipulation ** See Copyright Notice in lua.h */ - -#include - #define ltablib_c #define LUA_LIB +#include "lprefix.h" + + +#include +#include +#include + #include "lua.h" #include "lauxlib.h" #include "lualib.h" -#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_len(L, n)) +/* +** Operations that an object must define to mimic a table +** (some functions only need some of them) +*/ +#define TAB_R 1 /* read */ +#define TAB_W 2 /* write */ +#define TAB_L 4 /* length */ +#define TAB_RW (TAB_R | TAB_W) /* read/write */ + +#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) + + +static int checkfield (lua_State *L, const char *key, int n) { + lua_pushstring(L, key); + return (lua_rawget(L, -n) != LUA_TNIL); +} + + +/* +** Check that 'arg' either is a table or can behave like one (that is, +** has a metatable with the required metamethods) +*/ +static void checktab (lua_State *L, int arg, int what) { + if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ + int n = 1; /* number of elements to pop */ + if (lua_getmetatable(L, arg) && /* must have metatable */ + (!(what & TAB_R) || checkfield(L, "__index", ++n)) && + (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && + (!(what & TAB_L) || checkfield(L, "__len", ++n))) { + lua_pop(L, n); /* pop metatable and tested metamethods */ + } + else + luaL_argerror(L, arg, "table expected"); /* force an error */ + } +} #if defined(LUA_COMPAT_MAXN) @@ -39,65 +77,102 @@ static int maxn (lua_State *L) { static int tinsert (lua_State *L) { - int e = aux_getn(L, 1) + 1; /* first empty element */ - int pos; /* where to insert new element */ + lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */ + lua_Integer pos; /* where to insert new element */ switch (lua_gettop(L)) { case 2: { /* called with only 2 arguments */ pos = e; /* insert new element at the end */ break; } case 3: { - int i; - pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + lua_Integer i; + pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds"); for (i = e; i > pos; i--) { /* move up elements */ - lua_rawgeti(L, 1, i-1); - lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ + lua_geti(L, 1, i - 1); + lua_seti(L, 1, i); /* t[i] = t[i - 1] */ } break; } default: { - return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); + return luaL_error(L, "wrong number of arguments to 'insert'"); } } - lua_rawseti(L, 1, pos); /* t[pos] = v */ + lua_seti(L, 1, pos); /* t[pos] = v */ return 0; } static int tremove (lua_State *L) { - int size = aux_getn(L, 1); - int pos = luaL_optint(L, 2, size); + lua_Integer size = aux_getn(L, 1, TAB_RW); + lua_Integer pos = luaL_optinteger(L, 2, size); if (pos != size) /* validate 'pos' if given */ luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds"); - lua_rawgeti(L, 1, pos); /* result = t[pos] */ + lua_geti(L, 1, pos); /* result = t[pos] */ for ( ; pos < size; pos++) { - lua_rawgeti(L, 1, pos+1); - lua_rawseti(L, 1, pos); /* t[pos] = t[pos+1] */ + lua_geti(L, 1, pos + 1); + lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ } lua_pushnil(L); - lua_rawseti(L, 1, pos); /* t[pos] = nil */ + lua_seti(L, 1, pos); /* t[pos] = nil */ return 1; } -static void addfield (lua_State *L, luaL_Buffer *b, int i) { - lua_rawgeti(L, 1, i); +/* +** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever +** possible, copy in increasing order, which is better for rehashing. +** "possible" means destination after original range, or smaller +** than origin, or copying to another table. +*/ +static int tmove (lua_State *L) { + lua_Integer f = luaL_checkinteger(L, 2); + lua_Integer e = luaL_checkinteger(L, 3); + lua_Integer t = luaL_checkinteger(L, 4); + int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ + checktab(L, 1, TAB_R); + checktab(L, tt, TAB_W); + if (e >= f) { /* otherwise, nothing to move */ + lua_Integer n, i; + luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, + "too many elements to move"); + n = e - f + 1; /* number of elements to move */ + luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, + "destination wrap around"); + if (t > e || t <= f || tt != 1) { + for (i = 0; i < n; i++) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + else { + for (i = n - 1; i >= 0; i--) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + } + lua_pushvalue(L, tt); /* return "to table" */ + return 1; +} + + +static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { + lua_geti(L, 1, i); if (!lua_isstring(L, -1)) - luaL_error(L, "invalid value (%s) at index %d in table for " - LUA_QL("concat"), luaL_typename(L, -1), i); + luaL_error(L, "invalid value (%s) at index %d in table for 'concat'", + luaL_typename(L, -1), i); luaL_addvalue(b); } static int tconcat (lua_State *L) { luaL_Buffer b; + lua_Integer last = aux_getn(L, 1, TAB_R); size_t lsep; - int i, last; const char *sep = luaL_optlstring(L, 2, "", &lsep); - luaL_checktype(L, 1, LUA_TTABLE); - i = luaL_optint(L, 3, 1); - last = luaL_opt(L, luaL_checkint, 4, luaL_len(L, 1)); + lua_Integer i = luaL_optinteger(L, 3, 1); + last = luaL_opt(L, luaL_checkinteger, 4, last); luaL_buffinit(L, &b); for (; i < last; i++) { addfield(L, &b, i); @@ -117,35 +192,31 @@ static int tconcat (lua_State *L) { */ static int pack (lua_State *L) { + int i; int n = lua_gettop(L); /* number of elements to pack */ lua_createtable(L, n, 1); /* create result table */ + lua_insert(L, 1); /* put it at index 1 */ + for (i = n; i >= 1; i--) /* assign elements */ + lua_seti(L, 1, i); lua_pushinteger(L, n); - lua_setfield(L, -2, "n"); /* t.n = number of elements */ - if (n > 0) { /* at least one element? */ - int i; - lua_pushvalue(L, 1); - lua_rawseti(L, -2, 1); /* insert first element */ - lua_replace(L, 1); /* move table into index 1 */ - for (i = n; i >= 2; i--) /* assign other elements */ - lua_rawseti(L, 1, i); - } + lua_setfield(L, 1, "n"); /* t.n = number of elements */ return 1; /* return table */ } static int unpack (lua_State *L) { - int i, e, n; - luaL_checktype(L, 1, LUA_TTABLE); - i = luaL_optint(L, 2, 1); - e = luaL_opt(L, luaL_checkint, 3, luaL_len(L, 1)); + lua_Unsigned n; + lua_Integer i = luaL_optinteger(L, 2, 1); + lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); if (i > e) return 0; /* empty range */ - n = e - i + 1; /* number of elements */ - if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */ + n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ + if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) return luaL_error(L, "too many results to unpack"); - lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ - while (i++ < e) /* push arg[i + 1...e] */ - lua_rawgeti(L, 1, i); - return n; + for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ + lua_geti(L, 1, i); + } + lua_geti(L, 1, e); /* push last element */ + return (int)n; } /* }====================================================== */ @@ -155,102 +226,196 @@ static int unpack (lua_State *L) { /* ** {====================================================== ** Quicksort -** (based on `Algorithms in MODULA-3', Robert Sedgewick; +** (based on 'Algorithms in MODULA-3', Robert Sedgewick; ** Addison-Wesley, 1993.) ** ======================================================= */ -static void set2 (lua_State *L, int i, int j) { - lua_rawseti(L, 1, i); - lua_rawseti(L, 1, j); +/* +** Produce a "random" 'unsigned int' to randomize pivot choice. This +** macro is used only when 'sort' detects a big imbalance in the result +** of a partition. (If you don't want/need this "randomness", ~0 is a +** good choice.) +*/ +#if !defined(l_randomizePivot) /* { */ + +#include + +/* size of 'e' measured in number of 'unsigned int's */ +#define sof(e) (sizeof(e) / sizeof(unsigned int)) + +/* +** Use 'time' and 'clock' as sources of "randomness". Because we don't +** know the types 'clock_t' and 'time_t', we cannot cast them to +** anything without risking overflows. A safe way to use their values +** is to copy them to an array of a known type and use the array values. +*/ +static unsigned int l_randomizePivot (void) { + clock_t c = clock(); + time_t t = time(NULL); + unsigned int buff[sof(c) + sof(t)]; + unsigned int i, rnd = 0; + memcpy(buff, &c, sof(c) * sizeof(unsigned int)); + memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); + for (i = 0; i < sof(buff); i++) + rnd += buff[i]; + return rnd; } +#endif /* } */ + + +/* arrays larger than 'RANLIMIT' may use randomized pivots */ +#define RANLIMIT 100u + + +static void set2 (lua_State *L, unsigned int i, unsigned int j) { + lua_seti(L, 1, i); + lua_seti(L, 1, j); +} + + +/* +** Return true iff value at stack index 'a' is less than the value at +** index 'b' (according to the order of the sort). +*/ static int sort_comp (lua_State *L, int a, int b) { - if (!lua_isnil(L, 2)) { /* function? */ + if (lua_isnil(L, 2)) /* no function? */ + return lua_compare(L, a, b, LUA_OPLT); /* a < b */ + else { /* function */ int res; - lua_pushvalue(L, 2); + lua_pushvalue(L, 2); /* push function */ lua_pushvalue(L, a-1); /* -1 to compensate function */ - lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */ - lua_call(L, 2, 1); - res = lua_toboolean(L, -1); - lua_pop(L, 1); + lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ + lua_call(L, 2, 1); /* call function */ + res = lua_toboolean(L, -1); /* get result */ + lua_pop(L, 1); /* pop result */ return res; } - else /* a < b? */ - return lua_compare(L, a, b, LUA_OPLT); } -static void auxsort (lua_State *L, int l, int u) { - while (l < u) { /* for tail recursion */ - int i, j; - /* sort elements a[l], a[(l+u)/2] and a[u] */ - lua_rawgeti(L, 1, l); - lua_rawgeti(L, 1, u); - if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ - set2(L, l, u); /* swap a[l] - a[u] */ + +/* +** Does the partition: Pivot P is at the top of the stack. +** precondition: a[lo] <= P == a[up-1] <= a[up], +** so it only needs to do the partition from lo + 1 to up - 2. +** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] +** returns 'i'. +*/ +static unsigned int partition (lua_State *L, unsigned int lo, + unsigned int up) { + unsigned int i = lo; /* will be incremented before first use */ + unsigned int j = up - 1; /* will be decremented before first use */ + /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ + for (;;) { + /* next loop: repeat ++i while a[i] < P */ + while (lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ + /* next loop: repeat --j while P < a[j] */ + while (lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j < i) /* j < i but a[j] > P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ + if (j < i) { /* no elements out of place? */ + /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ + lua_pop(L, 1); /* pop a[j] */ + /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ + set2(L, up - 1, i); + return i; + } + /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ + set2(L, i, j); + } +} + + +/* +** Choose an element in the middle (2nd-3th quarters) of [lo,up] +** "randomized" by 'rnd' +*/ +static unsigned int choosePivot (unsigned int lo, unsigned int up, + unsigned int rnd) { + unsigned int r4 = (unsigned int)(up - lo) / 4u; /* range/4 */ + unsigned int p = rnd % (r4 * 2) + (lo + r4); + lua_assert(lo + r4 <= p && p <= up - r4); + return p; +} + + +/* +** QuickSort algorithm (recursive function) +*/ +static void auxsort (lua_State *L, unsigned int lo, unsigned int up, + unsigned int rnd) { + while (lo < up) { /* loop for tail recursion */ + unsigned int p; /* Pivot index */ + unsigned int n; /* to be used later */ + /* sort elements 'lo', 'p', and 'up' */ + lua_geti(L, 1, lo); + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ + set2(L, lo, up); /* swap a[lo] - a[up] */ else - lua_pop(L, 2); - if (u-l == 1) break; /* only 2 elements */ - i = (l+u)/2; - lua_rawgeti(L, 1, i); - lua_rawgeti(L, 1, l); - if (sort_comp(L, -2, -1)) /* a[i]= P */ - while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i>=u) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[i] */ - } - /* repeat --j until a[j] <= P */ - while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { - if (j<=l) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[j] */ - } - if (j n) /* partition too imbalanced? */ + rnd = l_randomizePivot(); /* try a new randomization */ + } /* tail call auxsort(L, lo, up, rnd) */ } + static int sort (lua_State *L) { - int n = aux_getn(L, 1); - luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */ - if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_settop(L, 2); /* make sure there is two arguments */ - auxsort(L, 1, n); + lua_Integer n = aux_getn(L, 1, TAB_RW); + if (n > 1) { /* non-trivial interval? */ + luaL_argcheck(L, n < INT_MAX, 1, "array too big"); + luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */ + if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ + lua_settop(L, 2); /* make sure there are two arguments */ + auxsort(L, 1, (unsigned int)n, 0u); + } return 0; } @@ -266,6 +431,7 @@ static const luaL_Reg tab_funcs[] = { {"pack", pack}, {"unpack", unpack}, {"remove", tremove}, + {"move", tmove}, {"sort", sort}, {NULL, NULL} }; diff --git a/deps/lua/ltm.c b/deps/lua/ltm.c index 69b4ed7..22b4df3 100644 --- a/deps/lua/ltm.c +++ b/deps/lua/ltm.c @@ -1,22 +1,27 @@ /* -** $Id: ltm.c,v 2.14.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ltm.c,v 2.36 2015/11/03 15:47:30 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ - -#include - #define ltm_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" +#include "ldebug.h" +#include "ldo.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" +#include "lvm.h" static const char udatatypename[] = "userdata"; @@ -25,7 +30,7 @@ LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = { "no value", "nil", "boolean", udatatypename, "number", "string", "table", "function", udatatypename, "thread", - "proto", "upval" /* these last two cases are used for tests only */ + "proto" /* this last case is used for tests only */ }; @@ -33,14 +38,16 @@ void luaT_init (lua_State *L) { static const char *const luaT_eventname[] = { /* ORDER TM */ "__index", "__newindex", "__gc", "__mode", "__len", "__eq", - "__add", "__sub", "__mul", "__div", "__mod", - "__pow", "__unm", "__lt", "__le", + "__add", "__sub", "__mul", "__mod", "__pow", + "__div", "__idiv", + "__band", "__bor", "__bxor", "__shl", "__shr", + "__unm", "__bnot", "__lt", "__le", "__concat", "__call" }; int i; for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); - luaS_fix(G(L)->tmname[i]); /* never collect these names */ + luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ } } @@ -50,7 +57,7 @@ void luaT_init (lua_State *L) { ** tag methods */ const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { - const TValue *tm = luaH_getstr(events, ename); + const TValue *tm = luaH_getshortstr(events, ename); lua_assert(event <= TM_EQ); if (ttisnil(tm)) { /* no tag method? */ events->flags |= cast_byte(1u<metatable; break; @@ -70,8 +77,73 @@ const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) { mt = uvalue(o)->metatable; break; default: - mt = G(L)->mt[ttypenv(o)]; + mt = G(L)->mt[ttnov(o)]; + } + return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : luaO_nilobject); +} + + +void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, TValue *p3, int hasres) { + ptrdiff_t result = savestack(L, p3); + StkId func = L->top; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + L->top += 3; + if (!hasres) /* no result? 'p3' is third argument */ + setobj2s(L, L->top++, p3); /* 3rd argument */ + /* metamethod may yield only when called from Lua code */ + if (isLua(L->ci)) + luaD_call(L, func, hasres); + else + luaD_callnoyield(L, func, hasres); + if (hasres) { /* if has result, move it to its place */ + p3 = restorestack(L, result); + setobjs2s(L, p3, --L->top); } - return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); +} + + +int luaT_callbinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (ttisnil(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (ttisnil(tm)) return 0; + luaT_callTM(L, tm, p1, p2, res, 1); + return 1; +} + + +void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + if (!luaT_callbinTM(L, p1, p2, res, event)) { + switch (event) { + case TM_CONCAT: + luaG_concaterror(L, p1, p2); + /* call never returns, but to avoid warnings: *//* FALLTHROUGH */ + case TM_BAND: case TM_BOR: case TM_BXOR: + case TM_SHL: case TM_SHR: case TM_BNOT: { + lua_Number dummy; + if (tonumber(p1, &dummy) && tonumber(p2, &dummy)) + luaG_tointerror(L, p1, p2); + else + luaG_opinterror(L, p1, p2, "perform bitwise operation on"); + } + /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ + default: + luaG_opinterror(L, p1, p2, "perform arithmetic on"); + } + } +} + + +int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, + TMS event) { + if (!luaT_callbinTM(L, p1, p2, L->top, event)) + return -1; /* no metamethod */ + else + return !l_isfalse(L->top); } diff --git a/deps/lua/ltm.h b/deps/lua/ltm.h index 7f89c84..180179c 100644 --- a/deps/lua/ltm.h +++ b/deps/lua/ltm.h @@ -1,5 +1,5 @@ /* -** $Id: ltm.h,v 2.11.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: ltm.h,v 2.21 2014/10/25 11:50:46 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ @@ -13,7 +13,7 @@ /* * WARNING: if you change the order of this enumeration, -* grep "ORDER TM" +* grep "ORDER TM" and "ORDER OP" */ typedef enum { TM_INDEX, @@ -21,14 +21,21 @@ typedef enum { TM_GC, TM_MODE, TM_LEN, - TM_EQ, /* last tag method with `fast' access */ + TM_EQ, /* last tag method with fast access */ TM_ADD, TM_SUB, TM_MUL, - TM_DIV, TM_MOD, TM_POW, + TM_DIV, + TM_IDIV, + TM_BAND, + TM_BOR, + TM_BXOR, + TM_SHL, + TM_SHR, TM_UNM, + TM_BNOT, TM_LT, TM_LE, TM_CONCAT, @@ -44,7 +51,7 @@ typedef enum { #define fasttm(l,et,e) gfasttm(G(l), et, e) #define ttypename(x) luaT_typenames_[(x) + 1] -#define objtypename(x) ttypename(ttypenv(x)) +#define objtypename(x) ttypename(ttnov(x)) LUAI_DDEC const char *const luaT_typenames_[LUA_TOTALTAGS]; @@ -54,4 +61,15 @@ LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event); LUAI_FUNC void luaT_init (lua_State *L); +LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, TValue *p3, int hasres); +LUAI_FUNC int luaT_callbinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event); +LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event); +LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, + const TValue *p2, TMS event); + + + #endif diff --git a/deps/lua/lua.c b/deps/lua/lua.c index 4345e55..545d23d 100644 --- a/deps/lua/lua.c +++ b/deps/lua/lua.c @@ -1,17 +1,19 @@ /* -** $Id: lua.c,v 1.206.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lua.c,v 1.226 2015/08/14 19:11:20 roberto Exp $ ** Lua stand-alone interpreter ** See Copyright Notice in lua.h */ +#define lua_c + +#include "lprefix.h" + #include #include #include #include -#define lua_c - #include "lua.h" #include "lauxlib.h" @@ -31,28 +33,38 @@ #define LUA_MAXINPUT 512 #endif -#if !defined(LUA_INIT) -#define LUA_INIT "LUA_INIT" +#if !defined(LUA_INIT_VAR) +#define LUA_INIT_VAR "LUA_INIT" #endif -#define LUA_INITVERSION \ - LUA_INIT "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR +#define LUA_INITVARVERSION \ + LUA_INIT_VAR "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR /* ** lua_stdin_is_tty detects whether the standard input is a 'tty' (that ** is, whether we're running lua interactively). */ -#if defined(LUA_USE_ISATTY) +#if !defined(lua_stdin_is_tty) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + #include #define lua_stdin_is_tty() isatty(0) -#elif defined(LUA_WIN) + +#elif defined(LUA_USE_WINDOWS) /* }{ */ + #include -#include #define lua_stdin_is_tty() _isatty(_fileno(stdin)) -#else + +#else /* }{ */ + +/* ISO C definition */ #define lua_stdin_is_tty() 1 /* assume stdin is a tty */ -#endif + +#endif /* } */ + +#endif /* } */ /* @@ -61,26 +73,27 @@ ** lua_saveline defines how to "save" a read line in a "history". ** lua_freeline defines how to free a line read by lua_readline. */ -#if defined(LUA_USE_READLINE) +#if !defined(lua_readline) /* { */ + +#if defined(LUA_USE_READLINE) /* { */ -#include #include #include #define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) -#define lua_saveline(L,idx) \ - if (lua_rawlen(L,idx) > 0) /* non-empty line? */ \ - add_history(lua_tostring(L, idx)); /* add it to history */ +#define lua_saveline(L,line) ((void)L, add_history(line)) #define lua_freeline(L,b) ((void)L, free(b)) -#elif !defined(lua_readline) +#else /* }{ */ #define lua_readline(L,b,p) \ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ -#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_saveline(L,line) { (void)L; (void)line; } #define lua_freeline(L,b) { (void)L; (void)b; } -#endif +#endif /* } */ + +#endif /* } */ @@ -90,33 +103,40 @@ static lua_State *globalL = NULL; static const char *progname = LUA_PROGNAME; - +/* +** Hook set by signal function to stop the interpreter. +*/ static void lstop (lua_State *L, lua_Debug *ar) { (void)ar; /* unused arg. */ - lua_sethook(L, NULL, 0, 0); + lua_sethook(L, NULL, 0, 0); /* reset hook */ luaL_error(L, "interrupted!"); } +/* +** Function to be called at a C signal. Because a C signal cannot +** just change a Lua state (as there is no proper synchronization), +** this function only sets a hook that, when called, will stop the +** interpreter. +*/ static void laction (int i) { - signal(i, SIG_DFL); /* if another SIGINT happens before lstop, - terminate process (default action) */ + signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); } static void print_usage (const char *badoption) { - luai_writestringerror("%s: ", progname); + lua_writestringerror("%s: ", progname); if (badoption[1] == 'e' || badoption[1] == 'l') - luai_writestringerror("'%s' needs argument\n", badoption); + lua_writestringerror("'%s' needs argument\n", badoption); else - luai_writestringerror("unrecognized option '%s'\n", badoption); - luai_writestringerror( + lua_writestringerror("unrecognized option '%s'\n", badoption); + lua_writestringerror( "usage: %s [options] [script [args]]\n" "Available options are:\n" - " -e stat execute string " LUA_QL("stat") "\n" - " -i enter interactive mode after executing " LUA_QL("script") "\n" - " -l name require library " LUA_QL("name") "\n" + " -e stat execute string 'stat'\n" + " -i enter interactive mode after executing 'script'\n" + " -l name require library 'name'\n" " -v show version information\n" " -E ignore environment variables\n" " -- stop handling options\n" @@ -126,101 +146,114 @@ static void print_usage (const char *badoption) { } +/* +** Prints an error message, adding the program name in front of it +** (if present) +*/ static void l_message (const char *pname, const char *msg) { - if (pname) luai_writestringerror("%s: ", pname); - luai_writestringerror("%s\n", msg); + if (pname) lua_writestringerror("%s: ", pname); + lua_writestringerror("%s\n", msg); } +/* +** Check whether 'status' is not OK and, if so, prints the error +** message on the top of the stack. It assumes that the error object +** is a string, as it was either generated by Lua or by 'msghandler'. +*/ static int report (lua_State *L, int status) { - if (status != LUA_OK && !lua_isnil(L, -1)) { + if (status != LUA_OK) { const char *msg = lua_tostring(L, -1); - if (msg == NULL) msg = "(error object is not a string)"; l_message(progname, msg); - lua_pop(L, 1); - /* force a complete garbage collection in case of errors */ - lua_gc(L, LUA_GCCOLLECT, 0); + lua_pop(L, 1); /* remove message */ } return status; } -/* the next function is called unprotected, so it must avoid errors */ -static void finalreport (lua_State *L, int status) { - if (status != LUA_OK) { - const char *msg = (lua_type(L, -1) == LUA_TSTRING) ? lua_tostring(L, -1) - : NULL; - if (msg == NULL) msg = "(error object is not a string)"; - l_message(progname, msg); - lua_pop(L, 1); - } -} - - -static int traceback (lua_State *L) { +/* +** Message handler used to run all chunks +*/ +static int msghandler (lua_State *L) { const char *msg = lua_tostring(L, 1); - if (msg) - luaL_traceback(L, L, msg, 1); - else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */ - if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ - lua_pushliteral(L, "(no error message)"); + if (msg == NULL) { /* is error object not a string? */ + if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ + lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ + return 1; /* that is the message */ + else + msg = lua_pushfstring(L, "(error object is a %s value)", + luaL_typename(L, 1)); } - return 1; + luaL_traceback(L, L, msg, 1); /* append a standard traceback */ + return 1; /* return the traceback */ } +/* +** Interface to 'lua_pcall', which sets appropriate message function +** and C-signal handler. Used to run all chunks. +*/ static int docall (lua_State *L, int narg, int nres) { int status; int base = lua_gettop(L) - narg; /* function index */ - lua_pushcfunction(L, traceback); /* push traceback function */ - lua_insert(L, base); /* put it under chunk and args */ + lua_pushcfunction(L, msghandler); /* push message handler */ + lua_insert(L, base); /* put it under function and args */ globalL = L; /* to be available to 'laction' */ - signal(SIGINT, laction); + signal(SIGINT, laction); /* set C-signal handler */ status = lua_pcall(L, narg, nres, base); - signal(SIGINT, SIG_DFL); - lua_remove(L, base); /* remove traceback function */ + signal(SIGINT, SIG_DFL); /* reset C-signal handler */ + lua_remove(L, base); /* remove message handler from the stack */ return status; } static void print_version (void) { - luai_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); - luai_writeline(); + lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); + lua_writeline(); } -static int getargs (lua_State *L, char **argv, int n) { - int narg; - int i; - int argc = 0; - while (argv[argc]) argc++; /* count total number of arguments */ - narg = argc - (n + 1); /* number of arguments to the script */ - luaL_checkstack(L, narg + 3, "too many arguments to script"); - for (i=n+1; i < argc; i++) - lua_pushstring(L, argv[i]); - lua_createtable(L, narg, n + 1); - for (i=0; i < argc; i++) { +/* +** Create the 'arg' table, which stores all arguments from the +** command line ('argv'). It should be aligned so that, at index 0, +** it has 'argv[script]', which is the script name. The arguments +** to the script (everything after 'script') go to positive indices; +** other arguments (before the script name) go to negative indices. +** If there is no script name, assume interpreter's name as base. +*/ +static void createargtable (lua_State *L, char **argv, int argc, int script) { + int i, narg; + if (script == argc) script = 0; /* no script name? */ + narg = argc - (script + 1); /* number of positive indices */ + lua_createtable(L, narg, script + 1); + for (i = 0; i < argc; i++) { lua_pushstring(L, argv[i]); - lua_rawseti(L, -2, i - n); + lua_rawseti(L, -2, i - script); } - return narg; + lua_setglobal(L, "arg"); } -static int dofile (lua_State *L, const char *name) { - int status = luaL_loadfile(L, name); +static int dochunk (lua_State *L, int status) { if (status == LUA_OK) status = docall(L, 0, 0); return report(L, status); } +static int dofile (lua_State *L, const char *name) { + return dochunk(L, luaL_loadfile(L, name)); +} + + static int dostring (lua_State *L, const char *s, const char *name) { - int status = luaL_loadbuffer(L, s, strlen(s), name); - if (status == LUA_OK) status = docall(L, 0, 0); - return report(L, status); + return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); } +/* +** Calls 'require(name)' and stores the result in a global variable +** with the given name. +*/ static int dolibrary (lua_State *L, const char *name) { int status; lua_getglobal(L, "require"); @@ -232,6 +265,9 @@ static int dolibrary (lua_State *L, const char *name) { } +/* +** Returns the string to be used as a prompt by the interpreter. +*/ static const char *get_prompt (lua_State *L, int firstline) { const char *p; lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); @@ -244,6 +280,12 @@ static const char *get_prompt (lua_State *L, int firstline) { #define EOFMARK "" #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) + +/* +** Check whether 'status' signals a syntax error and the error +** message at the top of the stack ends with the above mark for +** incomplete statements. +*/ static int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; @@ -257,163 +299,230 @@ static int incomplete (lua_State *L, int status) { } +/* +** Prompt the user, read a line, and push it into the Lua stack. +*/ static int pushline (lua_State *L, int firstline) { char buffer[LUA_MAXINPUT]; char *b = buffer; size_t l; const char *prmt = get_prompt(L, firstline); int readstatus = lua_readline(L, b, prmt); - lua_pop(L, 1); /* remove result from 'get_prompt' */ if (readstatus == 0) - return 0; /* no input */ + return 0; /* no input (prompt will be popped by caller) */ + lua_pop(L, 1); /* remove prompt */ l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ - b[l-1] = '\0'; /* remove it */ - if (firstline && b[0] == '=') /* first line starts with `=' ? */ - lua_pushfstring(L, "return %s", b+1); /* change it to `return' */ + b[--l] = '\0'; /* remove it */ + if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ + lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ else - lua_pushstring(L, b); + lua_pushlstring(L, b, l); lua_freeline(L, b); return 1; } +/* +** Try to compile line on the stack as 'return ;'; on return, stack +** has either compiled chunk or original line (if compilation failed). +*/ +static int addreturn (lua_State *L) { + const char *line = lua_tostring(L, -1); /* original line */ + const char *retline = lua_pushfstring(L, "return %s;", line); + int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); + if (status == LUA_OK) { + lua_remove(L, -2); /* remove modified line */ + if (line[0] != '\0') /* non empty? */ + lua_saveline(L, line); /* keep history */ + } + else + lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ + return status; +} + + +/* +** Read multiple lines until a complete Lua statement +*/ +static int multiline (lua_State *L) { + for (;;) { /* repeat until gets a complete statement */ + size_t len; + const char *line = lua_tolstring(L, 1, &len); /* get what it has */ + int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ + if (!incomplete(L, status) || !pushline(L, 0)) { + lua_saveline(L, line); /* keep history */ + return status; /* cannot or should not try to add continuation line */ + } + lua_pushliteral(L, "\n"); /* add newline... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } +} + + +/* +** Read a line and try to load (compile) it first as an expression (by +** adding "return " in front of it) and second as a statement. Return +** the final status of load/call with the resulting function (if any) +** in the top of the stack. +*/ static int loadline (lua_State *L) { int status; lua_settop(L, 0); if (!pushline(L, 1)) return -1; /* no input */ - for (;;) { /* repeat until gets a complete line */ - size_t l; - const char *line = lua_tolstring(L, 1, &l); - status = luaL_loadbuffer(L, line, l, "=stdin"); - if (!incomplete(L, status)) break; /* cannot try to add lines? */ - if (!pushline(L, 0)) /* no more input? */ - return -1; - lua_pushliteral(L, "\n"); /* add a new line... */ - lua_insert(L, -2); /* ...between the two lines */ - lua_concat(L, 3); /* join them */ - } - lua_saveline(L, 1); - lua_remove(L, 1); /* remove line */ + if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ + status = multiline(L); /* try as command, maybe with continuation lines */ + lua_remove(L, 1); /* remove line from the stack */ + lua_assert(lua_gettop(L) == 1); return status; } -static void dotty (lua_State *L) { +/* +** Prints (calling the Lua 'print' function) any values on the stack +*/ +static void l_print (lua_State *L) { + int n = lua_gettop(L); + if (n > 0) { /* any result to be printed? */ + luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, n, 0, 0) != LUA_OK) + l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)", + lua_tostring(L, -1))); + } +} + + +/* +** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and +** print any results. +*/ +static void doREPL (lua_State *L) { int status; const char *oldprogname = progname; - progname = NULL; + progname = NULL; /* no 'progname' on errors in interactive mode */ while ((status = loadline(L)) != -1) { - if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); - report(L, status); - if (status == LUA_OK && lua_gettop(L) > 0) { /* any result to print? */ - luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); - lua_getglobal(L, "print"); - lua_insert(L, 1); - if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != LUA_OK) - l_message(progname, lua_pushfstring(L, - "error calling " LUA_QL("print") " (%s)", - lua_tostring(L, -1))); - } + if (status == LUA_OK) + status = docall(L, 0, LUA_MULTRET); + if (status == LUA_OK) l_print(L); + else report(L, status); } lua_settop(L, 0); /* clear stack */ - luai_writeline(); + lua_writeline(); progname = oldprogname; } -static int handle_script (lua_State *L, char **argv, int n) { +/* +** Push on the stack the contents of table 'arg' from 1 to #arg +*/ +static int pushargs (lua_State *L) { + int i, n; + if (lua_getglobal(L, "arg") != LUA_TTABLE) + luaL_error(L, "'arg' is not a table"); + n = (int)luaL_len(L, -1); + luaL_checkstack(L, n + 3, "too many arguments to script"); + for (i = 1; i <= n; i++) + lua_rawgeti(L, -i, i); + lua_remove(L, -i); /* remove table from the stack */ + return n; +} + + +static int handle_script (lua_State *L, char **argv) { int status; - const char *fname; - int narg = getargs(L, argv, n); /* collect arguments */ - lua_setglobal(L, "arg"); - fname = argv[n]; - if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0) + const char *fname = argv[0]; + if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) fname = NULL; /* stdin */ status = luaL_loadfile(L, fname); - lua_insert(L, -(narg+1)); - if (status == LUA_OK) - status = docall(L, narg, LUA_MULTRET); - else - lua_pop(L, narg); + if (status == LUA_OK) { + int n = pushargs(L); /* push arguments to script */ + status = docall(L, n, LUA_MULTRET); + } return report(L, status); } -/* check that argument has no extra characters at the end */ -#define noextrachars(x) {if ((x)[2] != '\0') return -1;} - - -/* indices of various argument indicators in array args */ -#define has_i 0 /* -i */ -#define has_v 1 /* -v */ -#define has_e 2 /* -e */ -#define has_E 3 /* -E */ - -#define num_has 4 /* number of 'has_*' */ +/* bits of various argument indicators in 'args' */ +#define has_error 1 /* bad option */ +#define has_i 2 /* -i */ +#define has_v 4 /* -v */ +#define has_e 8 /* -e */ +#define has_E 16 /* -E */ -static int collectargs (char **argv, int *args) { +/* +** Traverses all arguments from 'argv', returning a mask with those +** needed before running any Lua code (or an error code if it finds +** any invalid argument). 'first' returns the first not-handled argument +** (either the script name or a bad argument in case of error). +*/ +static int collectargs (char **argv, int *first) { + int args = 0; int i; for (i = 1; argv[i] != NULL; i++) { + *first = i; if (argv[i][0] != '-') /* not an option? */ - return i; - switch (argv[i][1]) { /* option */ - case '-': - noextrachars(argv[i]); - return (argv[i+1] != NULL ? i+1 : 0); - case '\0': - return i; + return args; /* stop handling options */ + switch (argv[i][1]) { /* else check option */ + case '-': /* '--' */ + if (argv[i][2] != '\0') /* extra characters after '--'? */ + return has_error; /* invalid option */ + *first = i + 1; + return args; + case '\0': /* '-' */ + return args; /* script "name" is '-' */ case 'E': - args[has_E] = 1; + if (argv[i][2] != '\0') /* extra characters after 1st? */ + return has_error; /* invalid option */ + args |= has_E; break; case 'i': - noextrachars(argv[i]); - args[has_i] = 1; /* go through */ + args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ case 'v': - noextrachars(argv[i]); - args[has_v] = 1; + if (argv[i][2] != '\0') /* extra characters after 1st? */ + return has_error; /* invalid option */ + args |= has_v; break; case 'e': - args[has_e] = 1; /* go through */ + args |= has_e; /* FALLTHROUGH */ case 'l': /* both options need an argument */ if (argv[i][2] == '\0') { /* no concatenated argument? */ i++; /* try next 'argv' */ if (argv[i] == NULL || argv[i][0] == '-') - return -(i - 1); /* no next argument or it is another option */ + return has_error; /* no next argument or it is another option */ } break; - default: /* invalid option; return its index... */ - return -i; /* ...as a negative value */ + default: /* invalid option */ + return has_error; } } - return 0; + *first = i; /* no script name */ + return args; } +/* +** Processes options 'e' and 'l', which involve running Lua code. +** Returns 0 if some code raises an error. +*/ static int runargs (lua_State *L, char **argv, int n) { int i; for (i = 1; i < n; i++) { - lua_assert(argv[i][0] == '-'); - switch (argv[i][1]) { /* option */ - case 'e': { - const char *chunk = argv[i] + 2; - if (*chunk == '\0') chunk = argv[++i]; - lua_assert(chunk != NULL); - if (dostring(L, chunk, "=(command line)") != LUA_OK) - return 0; - break; - } - case 'l': { - const char *filename = argv[i] + 2; - if (*filename == '\0') filename = argv[++i]; - lua_assert(filename != NULL); - if (dolibrary(L, filename) != LUA_OK) - return 0; /* stop if file fails */ - break; - } - default: break; + int option = argv[i][1]; + lua_assert(argv[i][0] == '-'); /* already checked */ + if (option == 'e' || option == 'l') { + int status; + const char *extra = argv[i] + 2; /* both options need an argument */ + if (*extra == '\0') extra = argv[++i]; + lua_assert(extra != NULL); + status = (option == 'e') + ? dostring(L, extra, "=(command line)") + : dolibrary(L, extra); + if (status != LUA_OK) return 0; } } return 1; @@ -421,10 +530,10 @@ static int runargs (lua_State *L, char **argv, int n) { static int handle_luainit (lua_State *L) { - const char *name = "=" LUA_INITVERSION; + const char *name = "=" LUA_INITVARVERSION; const char *init = getenv(name + 1); if (init == NULL) { - name = "=" LUA_INIT; + name = "=" LUA_INIT_VAR; init = getenv(name + 1); /* try alternative name */ } if (init == NULL) return LUA_OK; @@ -435,40 +544,44 @@ static int handle_luainit (lua_State *L) { } +/* +** Main body of stand-alone interpreter (to be called in protected mode). +** Reads the options and handles them all. +*/ static int pmain (lua_State *L) { int argc = (int)lua_tointeger(L, 1); char **argv = (char **)lua_touserdata(L, 2); int script; - int args[num_has]; - args[has_i] = args[has_v] = args[has_e] = args[has_E] = 0; + int args = collectargs(argv, &script); + luaL_checkversion(L); /* check that interpreter has correct version */ if (argv[0] && argv[0][0]) progname = argv[0]; - script = collectargs(argv, args); - if (script < 0) { /* invalid arg? */ - print_usage(argv[-script]); + if (args == has_error) { /* bad arg? */ + print_usage(argv[script]); /* 'script' has index of bad arg. */ return 0; } - if (args[has_v]) print_version(); - if (args[has_E]) { /* option '-E'? */ + if (args & has_v) /* option '-v'? */ + print_version(); + if (args & has_E) { /* option '-E'? */ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); } - /* open standard libraries */ - luaL_checkversion(L); - lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ - luaL_openlibs(L); /* open libraries */ - lua_gc(L, LUA_GCRESTART, 0); - if (!args[has_E] && handle_luainit(L) != LUA_OK) - return 0; /* error running LUA_INIT */ - /* execute arguments -e and -l */ - if (!runargs(L, argv, (script > 0) ? script : argc)) return 0; - /* execute main script (if there is one) */ - if (script && handle_script(L, argv, script) != LUA_OK) return 0; - if (args[has_i]) /* -i option? */ - dotty(L); - else if (script == 0 && !args[has_e] && !args[has_v]) { /* no arguments? */ - if (lua_stdin_is_tty()) { + luaL_openlibs(L); /* open standard libraries */ + createargtable(L, argv, argc, script); /* create table 'arg' */ + if (!(args & has_E)) { /* no option '-E'? */ + if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ + return 0; /* error running LUA_INIT */ + } + if (!runargs(L, argv, script)) /* execute arguments -e and -l */ + return 0; /* something failed */ + if (script < argc && /* execute main script (if there is one) */ + handle_script(L, argv + script) != LUA_OK) + return 0; + if (args & has_i) /* -i option? */ + doREPL(L); /* do read-eval-print loop */ + else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */ + if (lua_stdin_is_tty()) { /* running in interactive mode? */ print_version(); - dotty(L); + doREPL(L); /* do read-eval-print loop */ } else dofile(L, NULL); /* executes stdin as a file */ } @@ -484,13 +597,12 @@ int main (int argc, char **argv) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } - /* call 'pmain' in protected mode */ - lua_pushcfunction(L, &pmain); + lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushinteger(L, argc); /* 1st argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */ - status = lua_pcall(L, 2, 1, 0); + status = lua_pcall(L, 2, 1, 0); /* do the call */ result = lua_toboolean(L, -1); /* get result */ - finalreport(L, status); + report(L, status); lua_close(L); return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/deps/lua/lua.h b/deps/lua/lua.h index 2b19294..a6955cc 100644 --- a/deps/lua/lua.h +++ b/deps/lua/lua.h @@ -1,5 +1,5 @@ /* -** $Id: lua.h,v 1.285.1.2 2013/11/11 12:09:16 roberto Exp $ +** $Id: lua.h,v 1.329 2015/11/13 17:18:42 roberto Exp $ ** Lua - A Scripting Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file @@ -17,27 +17,29 @@ #define LUA_VERSION_MAJOR "5" -#define LUA_VERSION_MINOR "2" -#define LUA_VERSION_NUM 502 -#define LUA_VERSION_RELEASE "3" +#define LUA_VERSION_MINOR "3" +#define LUA_VERSION_NUM 503 +#define LUA_VERSION_RELEASE "2" #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2013 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2015 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" /* mark for precompiled code ('Lua') */ -#define LUA_SIGNATURE "\033Lua" +#define LUA_SIGNATURE "\x1bLua" /* option for multiple returns in 'lua_pcall' and 'lua_call' */ #define LUA_MULTRET (-1) /* -** pseudo-indices +** Pseudo-indices +** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty +** space after that to help overflow detection) */ -#define LUA_REGISTRYINDEX LUAI_FIRSTPSEUDOIDX +#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) @@ -53,22 +55,6 @@ typedef struct lua_State lua_State; -typedef int (*lua_CFunction) (lua_State *L); - - -/* -** functions that read/write blocks when loading/dumping Lua chunks -*/ -typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); - -typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); - - -/* -** prototype for memory-allocation functions -*/ -typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); - /* ** basic types @@ -109,6 +95,34 @@ typedef LUA_INTEGER lua_Integer; /* unsigned integer type */ typedef LUA_UNSIGNED lua_Unsigned; +/* type for continuation-function contexts */ +typedef LUA_KCONTEXT lua_KContext; + + +/* +** Type for C functions registered with Lua +*/ +typedef int (*lua_CFunction) (lua_State *L); + +/* +** Type for continuation functions +*/ +typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); + + +/* +** Type for functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); + + +/* +** Type for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + /* @@ -149,11 +163,9 @@ LUA_API int (lua_absindex) (lua_State *L, int idx); LUA_API int (lua_gettop) (lua_State *L); LUA_API void (lua_settop) (lua_State *L, int idx); LUA_API void (lua_pushvalue) (lua_State *L, int idx); -LUA_API void (lua_remove) (lua_State *L, int idx); -LUA_API void (lua_insert) (lua_State *L, int idx); -LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API void (lua_rotate) (lua_State *L, int idx, int n); LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); -LUA_API int (lua_checkstack) (lua_State *L, int sz); +LUA_API int (lua_checkstack) (lua_State *L, int n); LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); @@ -165,13 +177,13 @@ LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); LUA_API int (lua_isnumber) (lua_State *L, int idx); LUA_API int (lua_isstring) (lua_State *L, int idx); LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isinteger) (lua_State *L, int idx); LUA_API int (lua_isuserdata) (lua_State *L, int idx); LUA_API int (lua_type) (lua_State *L, int idx); LUA_API const char *(lua_typename) (lua_State *L, int tp); LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); -LUA_API lua_Unsigned (lua_tounsignedx) (lua_State *L, int idx, int *isnum); LUA_API int (lua_toboolean) (lua_State *L, int idx); LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); LUA_API size_t (lua_rawlen) (lua_State *L, int idx); @@ -185,13 +197,20 @@ LUA_API const void *(lua_topointer) (lua_State *L, int idx); ** Comparison and arithmetic functions */ -#define LUA_OPADD 0 /* ORDER TM */ +#define LUA_OPADD 0 /* ORDER TM, ORDER OP */ #define LUA_OPSUB 1 #define LUA_OPMUL 2 -#define LUA_OPDIV 3 -#define LUA_OPMOD 4 -#define LUA_OPPOW 5 -#define LUA_OPUNM 6 +#define LUA_OPMOD 3 +#define LUA_OPPOW 4 +#define LUA_OPDIV 5 +#define LUA_OPIDIV 6 +#define LUA_OPBAND 7 +#define LUA_OPBOR 8 +#define LUA_OPBXOR 9 +#define LUA_OPSHL 10 +#define LUA_OPSHR 11 +#define LUA_OPUNM 12 +#define LUA_OPBNOT 13 LUA_API void (lua_arith) (lua_State *L, int op); @@ -209,8 +228,7 @@ LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); LUA_API void (lua_pushnil) (lua_State *L); LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); -LUA_API void (lua_pushunsigned) (lua_State *L, lua_Unsigned n); -LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp); @@ -224,26 +242,29 @@ LUA_API int (lua_pushthread) (lua_State *L); /* ** get functions (Lua -> stack) */ -LUA_API void (lua_getglobal) (lua_State *L, const char *var); -LUA_API void (lua_gettable) (lua_State *L, int idx); -LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); -LUA_API void (lua_rawget) (lua_State *L, int idx); -LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); -LUA_API void (lua_rawgetp) (lua_State *L, int idx, const void *p); +LUA_API int (lua_getglobal) (lua_State *L, const char *name); +LUA_API int (lua_gettable) (lua_State *L, int idx); +LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawget) (lua_State *L, int idx); +LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); + LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); LUA_API int (lua_getmetatable) (lua_State *L, int objindex); -LUA_API void (lua_getuservalue) (lua_State *L, int idx); +LUA_API int (lua_getuservalue) (lua_State *L, int idx); /* ** set functions (stack -> Lua) */ -LUA_API void (lua_setglobal) (lua_State *L, const char *var); +LUA_API void (lua_setglobal) (lua_State *L, const char *name); LUA_API void (lua_settable) (lua_State *L, int idx); LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawset) (lua_State *L, int idx); -LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); LUA_API int (lua_setmetatable) (lua_State *L, int objindex); LUA_API void (lua_setuservalue) (lua_State *L, int idx); @@ -252,21 +273,18 @@ LUA_API void (lua_setuservalue) (lua_State *L, int idx); /* ** 'load' and 'call' functions (load and run Lua code) */ -LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, int ctx, - lua_CFunction k); +LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k); #define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) -LUA_API int (lua_getctx) (lua_State *L, int *ctx); - LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, - int ctx, lua_CFunction k); + lua_KContext ctx, lua_KFunction k); #define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, - const char *chunkname, - const char *mode); + const char *chunkname, const char *mode); -LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); /* Added by xdczju@sina.com */ #if defined(LUA_USE_SHARED_PROTO) @@ -279,17 +297,17 @@ LUA_API void (lua_refsharedclosure)(void *f); LUA_API void (lua_unrefsharedclosure)(void *f); #endif -/* Added by xdczju@sina.com */ -LUA_API int (lua_yieldable)(lua_State *L); - /* ** coroutine functions */ -LUA_API int (lua_yieldk) (lua_State *L, int nresults, int ctx, - lua_CFunction k); +LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k); +LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg); +LUA_API int (lua_status) (lua_State *L); +LUA_API int (lua_isyieldable) (lua_State *L); + #define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) -LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg); -LUA_API int (lua_status) (lua_State *L); + /* ** garbage-collection function and options @@ -303,10 +321,7 @@ LUA_API int (lua_status) (lua_State *L); #define LUA_GCSTEP 5 #define LUA_GCSETPAUSE 6 #define LUA_GCSETSTEPMUL 7 -#define LUA_GCSETMAJORINC 8 #define LUA_GCISRUNNING 9 -#define LUA_GCGEN 10 -#define LUA_GCINC 11 LUA_API int (lua_gc) (lua_State *L, int what, int data); @@ -322,20 +337,23 @@ LUA_API int (lua_next) (lua_State *L, int idx); LUA_API void (lua_concat) (lua_State *L, int n); LUA_API void (lua_len) (lua_State *L, int idx); +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); + LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); /* -** =============================================================== +** {============================================================== ** some useful macros ** =============================================================== */ -#define lua_tonumber(L,i) lua_tonumberx(L,i,NULL) -#define lua_tointeger(L,i) lua_tointegerx(L,i,NULL) -#define lua_tounsigned(L,i) lua_tounsignedx(L,i,NULL) +#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) + +#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) #define lua_pop(L,n) lua_settop(L, -(n)-1) @@ -354,8 +372,7 @@ LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); #define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) #define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) -#define lua_pushliteral(L, s) \ - lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) +#define lua_pushliteral(L, s) lua_pushstring(L, "" s) #define lua_pushglobaltable(L) \ lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) @@ -363,6 +380,28 @@ LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); #define lua_tostring(L,i) lua_tolstring(L, (i), NULL) +#define lua_insert(L,idx) lua_rotate(L, (idx), 1) + +#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) + +#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) + +/* }============================================================== */ + + +/* +** {============================================================== +** compatibility macros for unsigned conversions +** =============================================================== +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) +#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) +#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) + +#endif +/* }============================================================== */ /* ** {====================================================================== @@ -407,7 +446,7 @@ LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, int fidx2, int n2); -LUA_API int (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); LUA_API lua_Hook (lua_gethook) (lua_State *L); LUA_API int (lua_gethookmask) (lua_State *L); LUA_API int (lua_gethookcount) (lua_State *L); @@ -435,7 +474,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2013 Lua.org, PUC-Rio. +* Copyright (C) 1994-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 diff --git a/deps/lua/lua.vcxproj b/deps/lua/lua.vcxproj index e9e1ccd..452d78b 100644 --- a/deps/lua/lua.vcxproj +++ b/deps/lua/lua.vcxproj @@ -22,11 +22,11 @@ Unicode - Application + DynamicLibrary false v120 true - MultiByte + Unicode @@ -43,19 +43,24 @@ .dll nlua + + $(ProjectDir) + nlua + .dll + Level3 Disabled true - WIN32;_DEBUG;LUA_COMPAT_MODULE;LUA_BUILD_AS_DLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + _DEBUG;LUA_COMPAT_5_2;LUA_COMPAT_5_1;LUA_BUILD_AS_DLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) Default true - $(SolutionDir)install.bat $(SolutionDir) $(ProjectName) + copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir) @@ -65,12 +70,16 @@ true true true + LUA_COMPAT_5_2;LUA_COMPAT_5_1;LUA_BUILD_AS_DLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) true true true + + copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir) + @@ -103,6 +112,7 @@ + @@ -122,6 +132,7 @@ + diff --git a/deps/lua/lua.vcxproj.filters b/deps/lua/lua.vcxproj.filters index 912d92d..ae742ed 100644 --- a/deps/lua/lua.vcxproj.filters +++ b/deps/lua/lua.vcxproj.filters @@ -97,6 +97,9 @@ src + + src + @@ -177,6 +180,9 @@ src + + src + diff --git a/deps/lua/luac.c b/deps/lua/luac.c index 2059404..e9bfc9e 100644 --- a/deps/lua/luac.c +++ b/deps/lua/luac.c @@ -1,17 +1,20 @@ /* -** $Id: luac.c,v 1.69 2011/11/29 17:46:33 lhf Exp $ -** Lua compiler (saves bytecodes to files; also list bytecodes) +** $Id: luac.c,v 1.75 2015/03/12 01:58:27 lhf Exp $ +** Lua compiler (saves bytecodes to files; also lists bytecodes) ** See Copyright Notice in lua.h */ +#define luac_c +#define LUA_CORE + +#include "lprefix.h" + +#include #include #include #include #include -#define luac_c -#define LUA_CORE - #include "lua.h" #include "lauxlib.h" @@ -47,14 +50,14 @@ static void cannot(const char* what) static void usage(const char* message) { if (*message=='-') - fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message); + fprintf(stderr,"%s: unrecognized option '%s'\n",progname,message); else fprintf(stderr,"%s: %s\n",progname,message); fprintf(stderr, "usage: %s [options] [filenames]\n" "Available options are:\n" " -l list (use -l -l for full listing)\n" - " -o name output to file " LUA_QL("name") " (default is \"%s\")\n" + " -o name output to file 'name' (default is \"%s\")\n" " -p parse only\n" " -s strip debug information\n" " -v show version information\n" @@ -89,7 +92,7 @@ static int doargs(int argc, char* argv[]) { output=argv[++i]; if (output==NULL || *output==0 || (*output=='-' && output[1]!=0)) - usage(LUA_QL("-o") " needs argument"); + usage("'-o' needs argument"); if (IS("-")) output=NULL; } else if (IS("-p")) /* parse only */ @@ -203,7 +206,7 @@ int main(int argc, char* argv[]) } /* -** $Id: print.c,v 1.69 2013/07/04 01:03:46 lhf Exp $ +** $Id: luac.c,v 1.75 2015/03/12 01:58:27 lhf Exp $ ** print bytecodes ** See Copyright Notice in lua.h */ @@ -223,7 +226,7 @@ int main(int argc, char* argv[]) static void PrintString(const TString* ts) { const char* s=getstr(ts); - size_t i,n=ts->tsv.len; + size_t i,n=tsslen(ts); printf("%c",'"'); for (i=0; ik[i]; - switch (ttypenv(o)) + switch (ttype(o)) { case LUA_TNIL: printf("nil"); @@ -259,11 +262,19 @@ static void PrintConstant(const Proto* f, int i) case LUA_TBOOLEAN: printf(bvalue(o) ? "true" : "false"); break; - case LUA_TNUMBER: - printf(LUA_NUMBER_FMT,nvalue(o)); + case LUA_TNUMFLT: + { + char buff[100]; + sprintf(buff,LUA_NUMBER_FMT,fltvalue(o)); + printf("%s",buff); + if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0"); + break; + } + case LUA_TNUMINT: + printf(LUA_INTEGER_FMT,ivalue(o)); break; - case LUA_TSTRING: - PrintString(rawtsvalue(o)); + case LUA_TSHRSTR: case LUA_TLNGSTR: + PrintString(tsvalue(o)); break; default: /* cannot happen */ printf("? type=%d",ttype(o)); @@ -338,8 +349,14 @@ static void PrintCode(const Proto* f) case OP_ADD: case OP_SUB: case OP_MUL: - case OP_DIV: case OP_POW: + case OP_DIV: + case OP_IDIV: + case OP_BAND: + case OP_BOR: + case OP_BXOR: + case OP_SHL: + case OP_SHR: case OP_EQ: case OP_LT: case OP_LE: diff --git a/deps/lua/luaconf.h b/deps/lua/luaconf.h index 4a97ef2..5dce53e 100644 --- a/deps/lua/luaconf.h +++ b/deps/lua/luaconf.h @@ -1,89 +1,173 @@ /* -** $Id: luaconf.h,v 1.176.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: luaconf.h,v 1.254 2015/10/21 18:17:40 roberto Exp $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ -#ifndef lconfig_h -#define lconfig_h +#ifndef luaconf_h +#define luaconf_h #include #include /* -** ================================================================== +** =================================================================== ** Search for "@@" to find all configurable definitions. ** =================================================================== */ /* -@@ LUA_ANSI controls the use of non-ansi features. -** CHANGE it (define it) if you want Lua to avoid the use of any -** non-ansi feature or library. +** {==================================================================== +** System Configuration: macros to adapt (if needed) Lua to some +** particular platform, for instance compiling it with 32-bit numbers or +** restricting it to C89. +** ===================================================================== */ -#if !defined(LUA_ANSI) && defined(__STRICT_ANSI__) -#define LUA_ANSI -#endif + +/* +@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. You +** can also define LUA_32BITS in the make file, but changing here you +** ensure that all software connected to Lua will be compiled with the +** same configuration. +*/ +/* #define LUA_32BITS */ -#if !defined(LUA_ANSI) && defined(_WIN32) && !defined(_WIN32_WCE) -#define LUA_WIN /* enable goodies for regular Windows platforms */ -#endif +/* +@@ LUA_USE_C89 controls the use of non-ISO-C89 features. +** Define it if you want Lua to avoid the use of a few C99 features +** or Windows-specific features on Windows. +*/ +/* #define LUA_USE_C89 */ -#if defined(LUA_WIN) -#define LUA_DL_DLL -#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ + +/* +** By default, Lua on Windows use (some) specific Windows features +*/ +#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_USE_WINDOWS /* enable goodies for regular Windows */ #endif +#if defined(LUA_USE_WINDOWS) +#define LUA_DL_DLL /* enable support for DLL */ +#define LUA_USE_C89 /* broadly, Windows is C89 */ +#endif + #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #define LUA_USE_READLINE /* needs some extra libraries */ -#define LUA_USE_STRTODHEX /* assume 'strtod' handles hex formats */ -#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ -#define LUA_USE_LONGLONG /* assume support for long long */ #endif + #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX -#define LUA_USE_DLOPEN /* does not need -ldl */ +#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ #define LUA_USE_READLINE /* needs an extra library: -lreadline */ -#define LUA_USE_STRTODHEX /* assume 'strtod' handles hex formats */ -#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ -#define LUA_USE_LONGLONG /* assume support for long long */ +#endif + + +/* +@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for +** C89 ('long' and 'double'); Windows always has '__int64', so it does +** not need to use this case. +*/ +#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) +#define LUA_C89_NUMBERS #endif /* -@@ LUA_USE_POSIX includes all functionality listed as X/Open System -@* Interfaces Extension (XSI). -** CHANGE it (define it) if your system is XSI compatible. +@@ LUAI_BITSINT defines the (minimum) number of bits in an 'int'. */ -#if defined(LUA_USE_POSIX) -#define LUA_USE_MKSTEMP -#define LUA_USE_ISATTY -#define LUA_USE_POPEN -#define LUA_USE_ULONGJMP -#define LUA_USE_GMTIME_R +/* avoid undefined shifts */ +#if ((INT_MAX >> 15) >> 15) >= 1 +#define LUAI_BITSINT 32 +#else +/* 'int' always must have at least 16 bits */ +#define LUAI_BITSINT 16 #endif +/* +@@ LUA_INT_TYPE defines the type for Lua integers. +@@ LUA_FLOAT_TYPE defines the type for Lua floats. +** Lua should work fine with any mix of these options (if supported +** by your C compiler). The usual configurations are 64-bit integers +** and 'double' (the default), 32-bit integers and 'float' (for +** restricted platforms), and 'long'/'double' (for C compilers not +** compliant with C99, which may not have support for 'long long'). +*/ + +/* predefined options for LUA_INT_TYPE */ +#define LUA_INT_INT 1 +#define LUA_INT_LONG 2 +#define LUA_INT_LONGLONG 3 + +/* predefined options for LUA_FLOAT_TYPE */ +#define LUA_FLOAT_FLOAT 1 +#define LUA_FLOAT_DOUBLE 2 +#define LUA_FLOAT_LONGDOUBLE 3 + +#if defined(LUA_32BITS) /* { */ +/* +** 32-bit integers and 'float' +*/ +#if LUAI_BITSINT >= 32 /* use 'int' if big enough */ +#define LUA_INT_TYPE LUA_INT_INT +#else /* otherwise use 'long' */ +#define LUA_INT_TYPE LUA_INT_LONG +#endif +#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT + +#elif defined(LUA_C89_NUMBERS) /* }{ */ +/* +** largest types available for C89 ('long' and 'double') +*/ +#define LUA_INT_TYPE LUA_INT_LONG +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE + +#endif /* } */ + + +/* +** default configuration for 64-bit Lua ('long long' and 'double') +*/ +#if !defined(LUA_INT_TYPE) +#define LUA_INT_TYPE LUA_INT_LONGLONG +#endif + +#if !defined(LUA_FLOAT_TYPE) +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE +#endif + +/* }================================================================== */ + + + + +/* +** {================================================================== +** Configuration for Paths. +** =================================================================== +*/ /* @@ LUA_PATH_DEFAULT is the default path that Lua uses to look for -@* Lua libraries. +** Lua libraries. @@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for -@* C libraries. +** C libraries. ** CHANGE them if your machine has a non-conventional directory ** hierarchy or if you want to install your libraries in ** non-conventional directories. */ +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #if defined(_WIN32) /* { */ /* ** In Windows, any exclamation mark ('!') in the path is replaced by the @@ -91,21 +175,26 @@ */ #define LUA_LDIR "!\\lua\\" #define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" #define LUA_PATH_DEFAULT \ LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" ".\\?.lua" + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" #define LUA_CPATH_DEFAULT \ - LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll;" ".\\?.dll" + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll" #else /* }{ */ -#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "/" #define LUA_ROOT "/usr/local/" -#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR -#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" #define LUA_PATH_DEFAULT \ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" "./?.lua" + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + "./?.lua;" "./?/init.lua" #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" #endif /* } */ @@ -122,14 +211,14 @@ #define LUA_DIRSEP "/" #endif +/* }================================================================== */ + /* -@@ LUA_ENV is the name of the variable that holds the current -@@ environment, used to access global names. -** CHANGE it if you do not like this name. +** {================================================================== +** Marks for exported symbols in the C code +** =================================================================== */ -#define LUA_ENV "_ENV" - /* @@ LUA_API is a mark for all core API functions. @@ -162,10 +251,10 @@ /* @@ LUAI_FUNC is a mark for all extern functions that are not to be -@* exported to outside modules. +** exported to outside modules. @@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables -@* that are not to be exported to outside modules (LUAI_DDEF for -@* definitions and LUAI_DDEC for declarations). +** that are not to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). ** CHANGE them if you need to mark them in some special way. Elf/gcc ** (versions 3.2 and later) mark them as "hidden" to optimize access ** when Lua is compiled as a shared library. Not all elf targets support @@ -177,74 +266,61 @@ #if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ defined(__ELF__) /* { */ #define LUAI_FUNC __attribute__((visibility("hidden"))) extern -#define LUAI_DDEC LUAI_FUNC -#define LUAI_DDEF /* empty */ - #else /* }{ */ #define LUAI_FUNC extern -#define LUAI_DDEC extern -#define LUAI_DDEF /* empty */ #endif /* } */ +#define LUAI_DDEC LUAI_FUNC +#define LUAI_DDEF /* empty */ + +/* }================================================================== */ /* -@@ LUA_QL describes how error messages quote program elements. -** CHANGE it if you want a different appearance. +** {================================================================== +** Compatibility with previous versions +** =================================================================== */ -#define LUA_QL(x) "'" x "'" -#define LUA_QS LUA_QL("%s") - /* -@@ LUA_IDSIZE gives the maximum size for the description of the source -@* of a function in debug information. -** CHANGE it if you want a different size. +@@ LUA_COMPAT_5_2 controls other macros for compatibility with Lua 5.2. +@@ LUA_COMPAT_5_1 controls other macros for compatibility with Lua 5.1. +** You can define it to get all options, or change specific options +** to fit your specific needs. */ -#define LUA_IDSIZE 60 - +#if defined(LUA_COMPAT_5_2) /* { */ /* -@@ luai_writestring/luai_writeline define how 'print' prints its results. -** They are only used in libraries and the stand-alone program. (The #if -** avoids including 'stdio.h' everywhere.) +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. */ -#if defined(LUA_LIB) || defined(lua_c) -#include -#define luai_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) -#define luai_writeline() (luai_writestring("\n", 1), fflush(stdout)) -#endif +#define LUA_COMPAT_MATHLIB /* -@@ luai_writestringerror defines how to print error messages. -** (A format string with one argument is enough for Lua...) +@@ LUA_COMPAT_BITLIB controls the presence of library 'bit32'. */ -#define luai_writestringerror(s,p) \ - (fprintf(stderr, (s), (p)), fflush(stderr)) +#define LUA_COMPAT_BITLIB +/* +@@ LUA_COMPAT_IPAIRS controls the effectiveness of the __ipairs metamethod. +*/ +#define LUA_COMPAT_IPAIRS /* -@@ LUAI_MAXSHORTLEN is the maximum length for short strings, that is, -** strings that are internalized. (Cannot be smaller than reserved words -** or tags for metamethods, as these strings must be internalized; -** #("function") = 8, #("__newindex") = 10.) +@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for +** manipulating other integer types (lua_pushunsigned, lua_tounsigned, +** luaL_checkint, luaL_checklong, etc.) */ -#define LUAI_MAXSHORTLEN 40 +#define LUA_COMPAT_APIINTCASTS +#endif /* } */ -/* -** {================================================================== -** Compatibility with previous versions -** =================================================================== -*/ +#if defined(LUA_COMPAT_5_1) /* { */ -/* -@@ LUA_COMPAT_ALL controls all compatibility options. -** You can define it to get all options, or change specific options -** to fit your specific needs. -*/ -#if defined(LUA_COMPAT_ALL) /* { */ +/* Incompatibilities from 5.2 -> 5.3 */ +#define LUA_COMPAT_MATHLIB +#define LUA_COMPAT_APIINTCASTS /* @@ LUA_COMPAT_UNPACK controls the presence of global 'unpack'. @@ -305,237 +381,375 @@ #endif /* } */ + +/* +@@ LUA_COMPAT_FLOATSTRING makes Lua format integral floats without a +@@ a float mark ('.0'). +** This macro is not on by default even in compatibility mode, +** because this is not really an incompatibility. +*/ +/* #define LUA_COMPAT_FLOATSTRING */ + /* }================================================================== */ /* -@@ LUAI_BITSINT defines the number of bits in an int. -** CHANGE here if Lua cannot automatically detect the number of bits of -** your machine. Probably you do not need to change this. +** {================================================================== +** Configuration for Numbers. +** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* +** satisfy your needs. +** =================================================================== +*/ + +/* +@@ LUA_NUMBER is the floating-point type used by Lua. +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@@ over a floating number. +@@ l_mathlim(x) corrects limit name 'x' to the proper float type +** by prefixing it with one of FLT/DBL/LDBL. +@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. +@@ LUA_NUMBER_FMT is the format for writing floats. +@@ lua_number2str converts a float to a string. +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. +@@ lua_str2number converts a decimal numeric string to a number. */ -/* avoid overflows in comparison */ -#if INT_MAX-20 < 32760 /* { */ -#define LUAI_BITSINT 16 -#elif INT_MAX > 2147483640L /* }{ */ -/* int has at least 32 bits */ -#define LUAI_BITSINT 32 -#else /* }{ */ -#error "you must define LUA_BITSINT with number of bits in an integer" -#endif /* } */ +/* The following definitions are good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + +#define lua_number2str(s,sz,n) l_sprintf((s), sz, LUA_NUMBER_FMT, (n)) + /* -@@ LUA_INT32 is an signed integer with exactly 32 bits. -@@ LUAI_UMEM is an unsigned integer big enough to count the total -@* memory used by Lua. -@@ LUAI_MEM is a signed integer big enough to count the total memory -@* used by Lua. -** CHANGE here if for some weird reason the default definitions are not -** good enough for your machine. Probably you do not need to change -** this. +@@ lua_numbertointeger converts a float number to an integer, or +** returns 0 if float is not within the range of a lua_Integer. +** (The range comparisons are tricky because of rounding. The tests +** here assume a two-complement representation, where MININTEGER always +** has an exact representation as a float; MAXINTEGER may not have one, +** and therefore its conversion to float may have an ill-defined value.) */ -#if LUAI_BITSINT >= 32 /* { */ -#define LUA_INT32 int -#define LUAI_UMEM size_t -#define LUAI_MEM ptrdiff_t -#else /* }{ */ -/* 16-bit ints */ -#define LUA_INT32 long -#define LUAI_UMEM unsigned long -#define LUAI_MEM long -#endif /* } */ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + +/* now the variable definitions */ + +#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ + +#define LUA_NUMBER float + +#define l_mathlim(n) (FLT_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.7g" + +#define l_mathop(op) op##f + +#define lua_str2number(s,p) strtof((s), (p)) + + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ + +#define LUA_NUMBER long double + +#define l_mathlim(n) (LDBL_##n) + +#define LUAI_UACNUMBER long double + +#define LUA_NUMBER_FRMLEN "L" +#define LUA_NUMBER_FMT "%.19Lg" + +#define l_mathop(op) op##l + +#define lua_str2number(s,p) strtold((s), (p)) + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ + +#define LUA_NUMBER double + +#define l_mathlim(n) (DBL_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.14g" + +#define l_mathop(op) op + +#define lua_str2number(s,p) strtod((s), (p)) + +#else /* }{ */ + +#error "numeric float type not defined" + +#endif /* } */ + /* -@@ LUAI_MAXSTACK limits the size of the Lua stack. -** CHANGE it if you need a different limit. This limit is arbitrary; -** its only purpose is to stop Lua to consume unlimited stack -** space (and to reserve some numbers for pseudo-indices). +@@ LUA_INTEGER is the integer type used by Lua. +** +@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. +** +@@ LUAI_UACINT is the result of an 'usual argument conversion' +@@ over a lUA_INTEGER. +@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. +@@ LUA_INTEGER_FMT is the format for writing integers. +@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. +@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. +@@ lua_integer2str converts an integer to a string. */ -#if LUAI_BITSINT >= 32 -#define LUAI_MAXSTACK 1000000 -#else -#define LUAI_MAXSTACK 15000 -#endif -/* reserve some space for error handling */ -#define LUAI_FIRSTPSEUDOIDX (-LUAI_MAXSTACK - 1000) +/* The following definitions are good for most cases here */ +#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" +#define lua_integer2str(s,sz,n) l_sprintf((s), sz, LUA_INTEGER_FMT, (n)) +#define LUAI_UACINT LUA_INTEGER /* -@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. -** CHANGE it if it uses too much C-stack space. +** use LUAI_UACINT here to avoid problems with promotions (which +** can turn a comparison between unsigneds into a signed comparison) */ -#define LUAL_BUFFERSIZE BUFSIZ +#define LUA_UNSIGNED unsigned LUAI_UACINT +/* now the variable definitions */ + +#if LUA_INT_TYPE == LUA_INT_INT /* { int */ + +#define LUA_INTEGER int +#define LUA_INTEGER_FRMLEN "" + +#define LUA_MAXINTEGER INT_MAX +#define LUA_MININTEGER INT_MIN + +#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ + +#define LUA_INTEGER long +#define LUA_INTEGER_FRMLEN "l" + +#define LUA_MAXINTEGER LONG_MAX +#define LUA_MININTEGER LONG_MIN + +#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ + +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ +#if defined(LLONG_MAX) /* { */ +/* use ISO C99 stuff */ + +#define LUA_INTEGER long long +#define LUA_INTEGER_FRMLEN "ll" + +#define LUA_MAXINTEGER LLONG_MAX +#define LUA_MININTEGER LLONG_MIN + +#elif defined(LUA_USE_WINDOWS) /* }{ */ +/* in Windows, can use specific Windows types */ + +#define LUA_INTEGER __int64 +#define LUA_INTEGER_FRMLEN "I64" + +#define LUA_MAXINTEGER _I64_MAX +#define LUA_MININTEGER _I64_MIN + +#else /* }{ */ + +#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ + or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" + +#endif /* } */ + +#else /* }{ */ + +#error "numeric integer type not defined" + +#endif /* } */ + +/* }================================================================== */ /* ** {================================================================== -@@ LUA_NUMBER is the type of numbers in Lua. -** CHANGE the following definitions only if you want to build Lua -** with a number type different from double. You may also need to -** change lua_number2int & lua_number2integer. +** Dependencies with C99 and other C details ** =================================================================== */ -#define LUA_NUMBER_DOUBLE -#define LUA_NUMBER double - /* -@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' -@* over a number. +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) */ -#define LUAI_UACNUMBER double +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif /* -@@ LUA_NUMBER_SCAN is the format for reading numbers. -@@ LUA_NUMBER_FMT is the format for writing numbers. -@@ lua_number2str converts a number to a string. -@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +@@ lua_strx2number converts an hexadecimal numeric string to a number. +** In C99, 'strtod' does that conversion. Otherwise, you can +** leave 'lua_strx2number' undefined and Lua will provide its own +** implementation. */ -#define LUA_NUMBER_SCAN "%lf" -#define LUA_NUMBER_FMT "%.14g" -#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) -#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ +#if !defined(LUA_USE_C89) +#define lua_strx2number(s,p) lua_str2number(s,p) +#endif /* -@@ l_mathop allows the addition of an 'l' or 'f' to all math operations +@@ lua_number2strx converts a float to an hexadecimal numeric string. +** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +** Otherwise, you can leave 'lua_number2strx' undefined and Lua will +** provide its own implementation. */ -#define l_mathop(x) (x) +#if !defined(LUA_USE_C89) +#define lua_number2strx(L,b,sz,f,n) l_sprintf(b,sz,f,n) +#endif /* -@@ lua_str2number converts a decimal numeric string to a number. -@@ lua_strx2number converts an hexadecimal numeric string to a number. -** In C99, 'strtod' does both conversions. C89, however, has no function -** to convert floating hexadecimal strings to numbers. For these -** systems, you can leave 'lua_strx2number' undefined and Lua will -** provide its own implementation. +** 'strtof' and 'opf' variants for math functions are not valid in +** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the +** availability of these variants. ('math.h' is already included in +** all files that use these macros.) */ -#define lua_str2number(s,p) strtod((s), (p)) - -#if defined(LUA_USE_STRTODHEX) -#define lua_strx2number(s,p) strtod((s), (p)) +#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) +#undef l_mathop /* variants not available */ +#undef lua_str2number +#define l_mathop(op) (lua_Number)op /* no variant */ +#define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) #endif /* -@@ The luai_num* macros define the primitive operations over numbers. +@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation +** functions. It must be a numerical type; Lua will use 'intptr_t' if +** available, otherwise it will use 'ptrdiff_t' (the nearest thing to +** 'intptr_t' in C89) */ +#define LUA_KCONTEXT ptrdiff_t -/* the following operations need the math library */ -#if defined(lobject_c) || defined(lvm_c) -#include -#define luai_nummod(L,a,b) ((a) - l_mathop(floor)((a)/(b))*(b)) -#define luai_numpow(L,a,b) (l_mathop(pow)(a,b)) +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(INTPTR_MAX) /* even in C99 this type is optional */ +#undef LUA_KCONTEXT +#define LUA_KCONTEXT intptr_t #endif - -/* these are quite standard operations */ -#if defined(LUA_CORE) -#define luai_numadd(L,a,b) ((a)+(b)) -#define luai_numsub(L,a,b) ((a)-(b)) -#define luai_nummul(L,a,b) ((a)*(b)) -#define luai_numdiv(L,a,b) ((a)/(b)) -#define luai_numunm(L,a) (-(a)) -#define luai_numeq(a,b) ((a)==(b)) -#define luai_numlt(L,a,b) ((a)<(b)) -#define luai_numle(L,a,b) ((a)<=(b)) -#define luai_numisnan(L,a) (!luai_numeq((a), (a))) #endif - /* -@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. -** CHANGE that if ptrdiff_t is not adequate on your machine. (On most -** machines, ptrdiff_t gives a good choice between int or long.) +@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). +** Change that if you do not want to use C locales. (Code using this +** macro must include header 'locale.h'.) */ -#define LUA_INTEGER ptrdiff_t - -/* -@@ LUA_UNSIGNED is the integral type used by lua_pushunsigned/lua_tounsigned. -** It must have at least 32 bits. -*/ -#define LUA_UNSIGNED unsigned LUA_INT32 +#if !defined(lua_getlocaledecpoint) +#define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif +/* }================================================================== */ /* -** Some tricks with doubles +** {================================================================== +** Language Variations +** ===================================================================== */ -#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */ /* -** The next definitions activate some tricks to speed up the -** conversion from doubles to integer types, mainly to LUA_UNSIGNED. -** -@@ LUA_MSASMTRICK uses Microsoft assembler to avoid clashes with a -** DirectX idiosyncrasy. -** -@@ LUA_IEEE754TRICK uses a trick that should work on any machine -** using IEEE754 with a 32-bit integer type. -** -@@ LUA_IEEELL extends the trick to LUA_INTEGER; should only be -** defined when LUA_INTEGER is a 32-bit integer. -** -@@ LUA_IEEEENDIAN is the endianness of doubles in your machine -** (0 for little endian, 1 for big endian); if not defined, Lua will -** check it dynamically for LUA_IEEE754TRICK (but not for LUA_NANTRICK). -** -@@ LUA_NANTRICK controls the use of a trick to pack all types into -** a single double value, using NaN values to represent non-number -** values. The trick only works on 32-bit machines (ints and pointers -** are 32-bit values) with numbers represented as IEEE 754-2008 doubles -** with conventional endianess (12345678 or 87654321), in CPUs that do -** not produce signaling NaN values (all NaNs are quiet). +@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some +** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from +** numbers to strings. Define LUA_NOCVTS2N to turn off automatic +** coercion from strings to numbers. */ +/* #define LUA_NOCVTN2S */ +/* #define LUA_NOCVTS2N */ -/* Microsoft compiler on a Pentium (32 bit) ? */ -#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86) /* { */ - -#define LUA_MSASMTRICK -#define LUA_IEEEENDIAN 0 -#define LUA_NANTRICK +/* +@@ LUA_USE_APICHECK turns on several consistency checks on the C API. +** Define it as a help when debugging C code. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(l,e) assert(e) +#endif -/* pentium 32 bits? */ -#elif defined(__i386__) || defined(__i386) || defined(__X86__) /* }{ */ +/* }================================================================== */ -#define LUA_IEEE754TRICK -#define LUA_IEEELL -#define LUA_IEEEENDIAN 0 -#define LUA_NANTRICK -/* pentium 64 bits? */ -#elif defined(__x86_64) /* }{ */ +/* +** {================================================================== +** Macros that affect the API and must be stable (that is, must be the +** same when you compile Lua and when you compile code that links to +** Lua). You probably do not want/need to change them. +** ===================================================================== +*/ -#define LUA_IEEE754TRICK -#define LUA_IEEEENDIAN 0 +/* +@@ LUAI_MAXSTACK limits the size of the Lua stack. +** CHANGE it if you need a different limit. This limit is arbitrary; +** its only purpose is to stop Lua from consuming unlimited stack +** space (and to reserve some numbers for pseudo-indices). +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK 15000 +#endif -#elif defined(__POWERPC__) || defined(__ppc__) /* }{ */ -#define LUA_IEEE754TRICK -#define LUA_IEEEENDIAN 1 +/* +@@ LUA_EXTRASPACE defines the size of a raw memory area associated with +** a Lua state with very fast access. +** CHANGE it if you need a different size. +*/ +#define LUA_EXTRASPACE (sizeof(void *)) -#else /* }{ */ -/* assume IEEE754 and a 32-bit integer type */ -#define LUA_IEEE754TRICK +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@@ of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 -#endif /* } */ -#endif /* } */ +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +** CHANGE it if it uses too much C-stack space. (For long double, +** 'string.format("%.99f", 1e4932)' needs ~5030 bytes, so a +** smaller buffer would force a memory allocation for each call to +** 'string.format'.) +*/ +#if defined(LUA_FLOAT_LONGDOUBLE) +#define LUAL_BUFFERSIZE 8192 +#else +#define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer))) +#endif /* }================================================================== */ +/* +@@ LUA_QL describes how error messages quote program elements. +** Lua does not use these macros anymore; they are here for +** compatibility only. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + /* =================================================================== */ @@ -547,5 +761,7 @@ #define LUA_USE_SHARED_PROTO + + #endif diff --git a/deps/lua/lualib.h b/deps/lua/lualib.h index 4dcc53f..6544e97 100644 --- a/deps/lua/lualib.h +++ b/deps/lua/lualib.h @@ -1,5 +1,5 @@ /* -** $Id: lualib.h,v 1.43.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lualib.h,v 1.44 2014/02/06 17:32:33 roberto Exp $ ** Lua standard libraries ** See Copyright Notice in lua.h */ @@ -29,6 +29,9 @@ LUAMOD_API int (luaopen_os) (lua_State *L); #define LUA_STRLIBNAME "string" LUAMOD_API int (luaopen_string) (lua_State *L); +#define LUA_UTF8LIBNAME "utf8" +LUAMOD_API int (luaopen_utf8) (lua_State *L); + #define LUA_BITLIBNAME "bit32" LUAMOD_API int (luaopen_bit32) (lua_State *L); diff --git a/deps/lua/lundump.c b/deps/lua/lundump.c index e47b29b..7cbf311 100644 --- a/deps/lua/lundump.c +++ b/deps/lua/lundump.c @@ -1,14 +1,17 @@ /* -** $Id: lundump.c,v 2.22.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lundump.c,v 2.44 2015/11/02 16:09:30 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ -#include - #define lundump_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "ldebug.h" @@ -20,244 +23,261 @@ #include "lundump.h" #include "lzio.h" + +#if !defined(luai_verifycode) +#define luai_verifycode(L,b,f) /* empty */ +#endif + + typedef struct { - lua_State* L; - ZIO* Z; - Mbuffer* b; - const char* name; + lua_State *L; + ZIO *Z; + const char *name; } LoadState; -static l_noret error(LoadState* S, const char* why) -{ - luaO_pushfstring(S->L,"%s: %s precompiled chunk",S->name,why); - luaD_throw(S->L,LUA_ERRSYNTAX); + +static l_noret error(LoadState *S, const char *why) { + luaO_pushfstring(S->L, "%s: %s precompiled chunk", S->name, why); + luaD_throw(S->L, LUA_ERRSYNTAX); } -#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) -#define LoadByte(S) (lu_byte)LoadChar(S) -#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) -#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) -#if !defined(luai_verifycode) -#define luai_verifycode(L,b,f) /* empty */ -#endif +/* +** All high-level loads go through LoadVector; you can change it to +** adapt to the endianness of the input +*/ +#define LoadVector(S,b,n) LoadBlock(S,b,(n)*sizeof((b)[0])) + +static void LoadBlock (LoadState *S, void *b, size_t size) { + if (luaZ_read(S->Z, b, size) != 0) + error(S, "truncated"); +} + -static void LoadBlock(LoadState* S, void* b, size_t size) -{ - if (luaZ_read(S->Z,b,size)!=0) error(S,"truncated"); +#define LoadVar(S,x) LoadVector(S,&x,1) + + +static lu_byte LoadByte (LoadState *S) { + lu_byte x; + LoadVar(S, x); + return x; } -static int LoadChar(LoadState* S) -{ - char x; - LoadVar(S,x); - return x; + +static int LoadInt (LoadState *S) { + int x; + LoadVar(S, x); + return x; } -static int LoadInt(LoadState* S) -{ - int x; - LoadVar(S,x); - if (x<0) error(S,"corrupted"); - return x; + +static lua_Number LoadNumber (LoadState *S) { + lua_Number x; + LoadVar(S, x); + return x; } -static lua_Number LoadNumber(LoadState* S) -{ - lua_Number x; - LoadVar(S,x); - return x; + +static lua_Integer LoadInteger (LoadState *S) { + lua_Integer x; + LoadVar(S, x); + return x; } #if defined(LoadString) #undef LoadString #endif -static TString* LoadString(LoadState* S) -{ - size_t size; - LoadVar(S,size); - if (size==0) - return NULL; - else - { - char* s=luaZ_openspace(S->L,S->b,size); - LoadBlock(S,s,size*sizeof(char)); - return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ - } +static TString *LoadString (LoadState *S) { + size_t size = LoadByte(S); + if (size == 0xFF) + LoadVar(S, size); + if (size == 0) + return NULL; + else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ + char buff[LUAI_MAXSHORTLEN]; + LoadVector(S, buff, size); + return luaS_newlstr(S->L, buff, size); + } + else { /* long string */ + TString *ts = luaS_createlngstrobj(S->L, size); + LoadVector(S, getstr(ts), size); /* load directly in final place */ + return ts; + } +} + + +static void LoadCode (LoadState *S, SharedProto *f) { + int n = LoadInt(S); + f->code = luaM_newvector(S->L, n, Instruction); + f->sizecode = n; + LoadVector(S, f->code, n); } -static void LoadCode(LoadState* S, SharedProto* f) -{ - int n=LoadInt(S); - f->code=luaM_newvector(S->L,n,Instruction); - f->sizecode=n; - LoadVector(S,f->code,n,sizeof(Instruction)); + +static void LoadFunction(LoadState *S, Proto *f, TString *psource); + + +static void LoadConstants (LoadState *S, Proto *f) { + int i; + int n = LoadInt(S); + f->k = luaM_newvector(S->L, n, TValue); + f->sp->sizek = n; + for (i = 0; i < n; i++) + setnilvalue(&f->k[i]); + for (i = 0; i < n; i++) { + TValue *o = &f->k[i]; + int t = LoadByte(S); + switch (t) { + case LUA_TNIL: + setnilvalue(o); + break; + case LUA_TBOOLEAN: + setbvalue(o, LoadByte(S)); + break; + case LUA_TNUMFLT: + setfltvalue(o, LoadNumber(S)); + break; + case LUA_TNUMINT: + setivalue(o, LoadInteger(S)); + break; + case LUA_TSHRSTR: + case LUA_TLNGSTR: + setsvalue2n(S->L, o, LoadString(S)); + break; + default: + lua_assert(0); + } + } } -static void LoadFunction(LoadState* S, Proto* f); - -static void LoadConstants(LoadState* S, Proto* f) -{ - int i,n; - n=LoadInt(S); - f->k=luaM_newvector(S->L,n,TValue); - f->sp->sizek=n; - for (i=0; ik[i]); - for (i=0; ik[i]; - int t=LoadChar(S); - switch (t) - { - case LUA_TNIL: - setnilvalue(o); - break; - case LUA_TBOOLEAN: - setbvalue(o,LoadChar(S)); - break; - case LUA_TNUMBER: - setnvalue(o,LoadNumber(S)); - break; - case LUA_TSTRING: - setsvalue2n(S->L,o,LoadString(S)); - break; - default: lua_assert(0); + +static void LoadProtos (LoadState *S, Proto *f) { + int i; + int n = LoadInt(S); + f->p = luaM_newvector(S->L, n, Proto *); + f->sp->sizep = n; + for (i = 0; i < n; i++) + f->p[i] = NULL; + for (i = 0; i < n; i++) { + f->p[i] = luaF_newproto(S->L, NULL); + LoadFunction(S, f->p[i], f->sp->source); } - } - n=LoadInt(S); - f->p=luaM_newvector(S->L,n,Proto*); - f->sp->sizep=n; - for (i=0; ip[i]=NULL; - for (i=0; ip[i]=luaF_newproto(S->L, NULL); - LoadFunction(S,f->p[i]); - } } -static void LoadUpvalues(LoadState* S, SharedProto* f) -{ - int i,n; - n=LoadInt(S); - f->upvalues=luaM_newvector(S->L,n,Upvaldesc); - f->sizeupvalues=n; - for (i=0; iupvalues[i].name=NULL; - for (i=0; iupvalues[i].instack=LoadByte(S); - f->upvalues[i].idx=LoadByte(S); - } + +static void LoadUpvalues (LoadState *S, SharedProto *f) { + int i, n; + n = LoadInt(S); + f->upvalues = luaM_newvector(S->L, n, Upvaldesc); + f->sizeupvalues = n; + for (i = 0; i < n; i++) + f->upvalues[i].name = NULL; + for (i = 0; i < n; i++) { + f->upvalues[i].instack = LoadByte(S); + f->upvalues[i].idx = LoadByte(S); + } } -static void LoadDebug(LoadState* S, SharedProto* f) -{ - int i,n; - f->source=LoadString(S); - n=LoadInt(S); - f->lineinfo=luaM_newvector(S->L,n,int); - f->sizelineinfo=n; - LoadVector(S,f->lineinfo,n,sizeof(int)); - n=LoadInt(S); - f->locvars=luaM_newvector(S->L,n,LocVar); - f->sizelocvars=n; - for (i=0; ilocvars[i].varname=NULL; - for (i=0; ilocvars[i].varname=LoadString(S); - f->locvars[i].startpc=LoadInt(S); - f->locvars[i].endpc=LoadInt(S); - } - n=LoadInt(S); - for (i=0; iupvalues[i].name=LoadString(S); + +static void LoadDebug (LoadState *S, SharedProto *f) { + int i, n; + n = LoadInt(S); + f->lineinfo = luaM_newvector(S->L, n, int); + f->sizelineinfo = n; + LoadVector(S, f->lineinfo, n); + n = LoadInt(S); + f->locvars = luaM_newvector(S->L, n, LocVar); + f->sizelocvars = n; + for (i = 0; i < n; i++) + f->locvars[i].varname = NULL; + for (i = 0; i < n; i++) { + f->locvars[i].varname = LoadString(S); + f->locvars[i].startpc = LoadInt(S); + f->locvars[i].endpc = LoadInt(S); + } + n = LoadInt(S); + for (i = 0; i < n; i++) + f->upvalues[i].name = LoadString(S); } -static void LoadFunction(LoadState* S, Proto* f) -{ - SharedProto *sp = f->sp; - sp->linedefined=LoadInt(S); - sp->lastlinedefined=LoadInt(S); - sp->numparams=LoadByte(S); - sp->is_vararg=LoadByte(S); - sp->maxstacksize=LoadByte(S); - LoadCode(S,sp); - LoadConstants(S,f); - LoadUpvalues(S,sp); - LoadDebug(S,sp); + +static void LoadFunction (LoadState *S, Proto *fp, TString *psource) { + SharedProto *f = fp->sp; + f->source = LoadString(S); + if (f->source == NULL) /* no source in dump? */ + f->source = psource; /* reuse parent's source */ + f->linedefined = LoadInt(S); + f->lastlinedefined = LoadInt(S); + f->numparams = LoadByte(S); + f->is_vararg = LoadByte(S); + f->maxstacksize = LoadByte(S); + LoadCode(S, f); + LoadConstants(S, fp); + LoadUpvalues(S, f); + LoadProtos(S, fp); + LoadDebug(S, f); } -/* the code below must be consistent with the code in luaU_header */ -#define N0 LUAC_HEADERSIZE -#define N1 (sizeof(LUA_SIGNATURE)-sizeof(char)) -#define N2 N1+2 -#define N3 N2+6 - -static void LoadHeader(LoadState* S) -{ - lu_byte h[LUAC_HEADERSIZE]; - lu_byte s[LUAC_HEADERSIZE]; - luaU_header(h); - memcpy(s,h,sizeof(char)); /* first char already read */ - LoadBlock(S,s+sizeof(char),LUAC_HEADERSIZE-sizeof(char)); - if (memcmp(h,s,N0)==0) return; - if (memcmp(h,s,N1)!=0) error(S,"not a"); - if (memcmp(h,s,N2)!=0) error(S,"version mismatch in"); - if (memcmp(h,s,N3)!=0) error(S,"incompatible"); else error(S,"corrupted"); + +static void checkliteral (LoadState *S, const char *s, const char *msg) { + char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */ + size_t len = strlen(s); + LoadVector(S, buff, len); + if (memcmp(s, buff, len) != 0) + error(S, msg); } -/* -** load precompiled chunk -*/ -Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) -{ - LoadState S; - Closure* cl; - if (*name=='@' || *name=='=') - S.name=name+1; - else if (*name==LUA_SIGNATURE[0]) - S.name="binary string"; - else - S.name=name; - S.L=L; - S.Z=Z; - S.b=buff; - LoadHeader(&S); - cl=luaF_newLclosure(L,1); - setclLvalue(L,L->top,cl); incr_top(L); - cl->l.p=luaF_newproto(L, NULL); - LoadFunction(&S,cl->l.p); - if (cl->l.p->sp->sizeupvalues != 1) - { - Proto* p=cl->l.p; - cl=luaF_newLclosure(L,cl->l.p->sp->sizeupvalues); - cl->l.p=p; - setclLvalue(L,L->top-1,cl); - } - luai_verifycode(L,buff,cl->l.p); - return cl; + +static void fchecksize (LoadState *S, size_t size, const char *tname) { + if (LoadByte(S) != size) + error(S, luaO_pushfstring(S->L, "%s size mismatch in", tname)); +} + + +#define checksize(S,t) fchecksize(S,sizeof(t),#t) + +static void checkHeader (LoadState *S) { + checkliteral(S, LUA_SIGNATURE + 1, "not a"); /* 1st char already checked */ + if (LoadByte(S) != LUAC_VERSION) + error(S, "version mismatch in"); + if (LoadByte(S) != LUAC_FORMAT) + error(S, "format mismatch in"); + checkliteral(S, LUAC_DATA, "corrupted"); + checksize(S, int); + checksize(S, size_t); + checksize(S, Instruction); + checksize(S, lua_Integer); + checksize(S, lua_Number); + if (LoadInteger(S) != LUAC_INT) + error(S, "endianness mismatch in"); + if (LoadNumber(S) != LUAC_NUM) + error(S, "float format mismatch in"); } -#define MYINT(s) (s[0]-'0') -#define VERSION MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR) -#define FORMAT 0 /* this is the official format */ /* -* make header for precompiled chunks -* if you change the code below be sure to update LoadHeader and FORMAT above -* and LUAC_HEADERSIZE in lundump.h +** load precompiled chunk */ -void luaU_header (lu_byte* h) -{ - int x=1; - memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-sizeof(char)); - h+=sizeof(LUA_SIGNATURE)-sizeof(char); - *h++=cast_byte(VERSION); - *h++=cast_byte(FORMAT); - *h++=cast_byte(*(char*)&x); /* endianness */ - *h++=cast_byte(sizeof(int)); - *h++=cast_byte(sizeof(size_t)); - *h++=cast_byte(sizeof(Instruction)); - *h++=cast_byte(sizeof(lua_Number)); - *h++=cast_byte(((lua_Number)0.5)==0); /* is lua_Number integral? */ - memcpy(h,LUAC_TAIL,sizeof(LUAC_TAIL)-sizeof(char)); +LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { + LoadState S; + LClosure *cl; + if (*name == '@' || *name == '=') + S.name = name + 1; + else if (*name == LUA_SIGNATURE[0]) + S.name = "binary string"; + else + S.name = name; + S.L = L; + S.Z = Z; + checkHeader(&S); + cl = luaF_newLclosure(L, LoadByte(&S)); + setclLvalue(L, L->top, cl); + luaD_inctop(L); + cl->p = luaF_newproto(L, NULL); + LoadFunction(&S, cl->p, NULL); + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luai_verifycode(L, buff, cl->p); + return cl; } + diff --git a/deps/lua/lundump.h b/deps/lua/lundump.h index 5255db2..aa5cc82 100644 --- a/deps/lua/lundump.h +++ b/deps/lua/lundump.h @@ -1,5 +1,5 @@ /* -** $Id: lundump.h,v 1.39.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lundump.h,v 1.45 2015/09/08 15:41:05 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ @@ -7,22 +7,26 @@ #ifndef lundump_h #define lundump_h +#include "llimits.h" #include "lobject.h" #include "lzio.h" -/* load one chunk; from lundump.c */ -LUAI_FUNC Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); -/* make header; from lundump.c */ -LUAI_FUNC void luaU_header (lu_byte* h); +/* data to catch conversion errors */ +#define LUAC_DATA "\x19\x93\r\n\x1a\n" -/* dump one chunk; from ldump.c */ -LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); +#define LUAC_INT 0x5678 +#define LUAC_NUM cast_num(370.5) -/* data to catch conversion errors */ -#define LUAC_TAIL "\x19\x93\r\n\x1a\n" +#define MYINT(s) (s[0]-'0') +#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR)) +#define LUAC_FORMAT 0 /* this is the official format */ -/* size in bytes of header of binary files */ -#define LUAC_HEADERSIZE (sizeof(LUA_SIGNATURE)-sizeof(char)+2+6+sizeof(LUAC_TAIL)-sizeof(char)) +/* load one chunk; from lundump.c */ +LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, + void* data, int strip); #endif diff --git a/deps/lua/lutf8lib.c b/deps/lua/lutf8lib.c new file mode 100644 index 0000000..9042582 --- /dev/null +++ b/deps/lua/lutf8lib.c @@ -0,0 +1,256 @@ +/* +** $Id: lutf8lib.c,v 1.15 2015/03/28 19:16:55 roberto Exp $ +** Standard library for UTF-8 manipulation +** See Copyright Notice in lua.h +*/ + +#define lutf8lib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + +#define MAXUNICODE 0x10FFFF + +#define iscont(p) ((*(p) & 0xC0) == 0x80) + + +/* from strlib */ +/* translate a relative string position: negative means back from end */ +static lua_Integer u_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; +} + + +/* +** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid. +*/ +static const char *utf8_decode (const char *o, int *val) { + static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF}; + const unsigned char *s = (const unsigned char *)o; + unsigned int c = s[0]; + unsigned int res = 0; /* final result */ + if (c < 0x80) /* ascii? */ + res = c; + else { + int count = 0; /* to count number of continuation bytes */ + while (c & 0x40) { /* still have continuation bytes? */ + int cc = s[++count]; /* read next byte */ + if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ + return NULL; /* invalid byte sequence */ + res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ + c <<= 1; /* to test next bit */ + } + res |= ((c & 0x7F) << (count * 5)); /* add first byte */ + if (count > 3 || res > MAXUNICODE || res <= limits[count]) + return NULL; /* invalid byte sequence */ + s += count; /* skip continuation bytes read */ + } + if (val) *val = res; + return (const char *)s + 1; /* +1 to include first byte */ +} + + +/* +** utf8len(s [, i [, j]]) --> number of characters that start in the +** range [i,j], or nil + current position if 's' is not well formed in +** that interval +*/ +static int utflen (lua_State *L) { + int n = 0; + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, + "initial position out of string"); + luaL_argcheck(L, --posj < (lua_Integer)len, 3, + "final position out of string"); + while (posi <= posj) { + const char *s1 = utf8_decode(s + posi, NULL); + if (s1 == NULL) { /* conversion error? */ + lua_pushnil(L); /* return nil ... */ + lua_pushinteger(L, posi + 1); /* ... and current position */ + return 2; + } + posi = s1 - s; + n++; + } + lua_pushinteger(L, n); + return 1; +} + + +/* +** codepoint(s, [i, [j]]) -> returns codepoints for all characters +** that start in the range [i,j] +*/ +static int codepoint (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); + int n; + const char *se; + luaL_argcheck(L, posi >= 1, 2, "out of range"); + luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range"); + if (posi > pose) return 0; /* empty interval; return no values */ + if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; + luaL_checkstack(L, n, "string slice too long"); + n = 0; + se = s + pose; + for (s += posi - 1; s < se;) { + int code; + s = utf8_decode(s, &code); + if (s == NULL) + return luaL_error(L, "invalid UTF-8 code"); + lua_pushinteger(L, code); + n++; + } + return n; +} + + +static void pushutfchar (lua_State *L, int arg) { + lua_Integer code = luaL_checkinteger(L, arg); + luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range"); + lua_pushfstring(L, "%U", (long)code); +} + + +/* +** utfchar(n1, n2, ...) -> char(n1)..char(n2)... +*/ +static int utfchar (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + if (n == 1) /* optimize common case of single char */ + pushutfchar(L, 1); + else { + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i = 1; i <= n; i++) { + pushutfchar(L, i); + luaL_addvalue(&b); + } + luaL_pushresult(&b); + } + return 1; +} + + +/* +** offset(s, n, [i]) -> index where n-th character counting from +** position 'i' starts; 0 means character at 'i'. +*/ +static int byteoffset (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = luaL_checkinteger(L, 2); + lua_Integer posi = (n >= 0) ? 1 : len + 1; + posi = u_posrelat(luaL_optinteger(L, 3, posi), len); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, + "position out of range"); + if (n == 0) { + /* find beginning of current byte sequence */ + while (posi > 0 && iscont(s + posi)) posi--; + } + else { + if (iscont(s + posi)) + luaL_error(L, "initial position is a continuation byte"); + if (n < 0) { + while (n < 0 && posi > 0) { /* move back */ + do { /* find beginning of previous character */ + posi--; + } while (posi > 0 && iscont(s + posi)); + n++; + } + } + else { + n--; /* do not move for 1st character */ + while (n > 0 && posi < (lua_Integer)len) { + do { /* find beginning of next character */ + posi++; + } while (iscont(s + posi)); /* (cannot pass final '\0') */ + n--; + } + } + } + if (n == 0) /* did it find given character? */ + lua_pushinteger(L, posi + 1); + else /* no such character */ + lua_pushnil(L); + return 1; +} + + +static int iter_aux (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = lua_tointeger(L, 2) - 1; + if (n < 0) /* first iteration? */ + n = 0; /* start from here */ + else if (n < (lua_Integer)len) { + n++; /* skip current byte */ + while (iscont(s + n)) n++; /* and its continuations */ + } + if (n >= (lua_Integer)len) + return 0; /* no more codepoints */ + else { + int code; + const char *next = utf8_decode(s + n, &code); + if (next == NULL || iscont(next)) + return luaL_error(L, "invalid UTF-8 code"); + lua_pushinteger(L, n + 1); + lua_pushinteger(L, code); + return 2; + } +} + + +static int iter_codes (lua_State *L) { + luaL_checkstring(L, 1); + lua_pushcfunction(L, iter_aux); + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); + return 3; +} + + +/* pattern to match a single UTF-8 character */ +#define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" + + +static const luaL_Reg funcs[] = { + {"offset", byteoffset}, + {"codepoint", codepoint}, + {"char", utfchar}, + {"len", utflen}, + {"codes", iter_codes}, + /* placeholders */ + {"charpattern", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_utf8 (lua_State *L) { + luaL_newlib(L, funcs); + lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1); + lua_setfield(L, -2, "charpattern"); + return 1; +} + diff --git a/deps/lua/lvm.c b/deps/lua/lvm.c index 43019d4..34293ee 100644 --- a/deps/lua/lvm.c +++ b/deps/lua/lvm.c @@ -1,17 +1,21 @@ /* -** $Id: lvm.c,v 2.155.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lvm.c,v 2.265 2015/11/23 11:30:45 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ +#define lvm_c +#define LUA_CORE + +#include "lprefix.h" +#include +#include +#include #include #include #include -#define lvm_c -#define LUA_CORE - #include "lua.h" #include "ldebug.h" @@ -27,304 +31,465 @@ #include "lvm.h" - /* limit for table tag-method chains (to avoid loops) */ -#define MAXTAGLOOP 100 +#define MAXTAGLOOP 2000 -const TValue *luaV_tonumber (const TValue *obj, TValue *n) { - lua_Number num; - if (ttisnumber(obj)) return obj; - if (ttisstring(obj) && luaO_str2d(svalue(obj), tsvalue(obj)->len, &num)) { - setnvalue(n, num); - return n; - } - else - return NULL; -} +/* +** 'l_intfitsf' checks whether a given integer can be converted to a +** float without rounding. Used in comparisons. Left undefined if +** all integers fit in a float precisely. +*/ +#if !defined(l_intfitsf) -int luaV_tostring (lua_State *L, StkId obj) { - if (!ttisnumber(obj)) - return 0; - else { - char s[LUAI_MAXNUMBER2STR]; - lua_Number n = nvalue(obj); - int l = lua_number2str(s, n); - setsvalue2s(L, obj, luaS_newlstr(L, s, l)); +/* number of bits in the mantissa of a float */ +#define NBM (l_mathlim(MANT_DIG)) + +/* +** Check whether some integers may not fit in a float, that is, whether +** (maxinteger >> NBM) > 0 (that implies (1 << NBM) <= maxinteger). +** (The shifts are done in parts to avoid shifting by more than the size +** of an integer. In a worst case, NBM == 113 for long double and +** sizeof(integer) == 32.) +*/ +#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \ + >> (NBM - (3 * (NBM / 4)))) > 0 + +#define l_intfitsf(i) \ + (-((lua_Integer)1 << NBM) <= (i) && (i) <= ((lua_Integer)1 << NBM)) + +#endif + +#endif + + + +/* +** Try to convert a value to a float. The float case is already handled +** by the macro 'tonumber'. +*/ +int luaV_tonumber_ (const TValue *obj, lua_Number *n) { + TValue v; + if (ttisinteger(obj)) { + *n = cast_num(ivalue(obj)); + return 1; + } + else if (cvt2num(obj) && /* string convertible to number? */ + luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) { + *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */ return 1; } + else + return 0; /* conversion failed */ } -static void traceexec (lua_State *L) { - CallInfo *ci = L->ci; - lu_byte mask = L->hookmask; - int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0); - if (counthook) - resethookcount(L); /* reset count */ - if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ - ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ - return; /* do not call hook again (VM yielded, so it did not move) */ +/* +** try to convert a value to an integer, rounding according to 'mode': +** mode == 0: accepts only integral values +** mode == 1: takes the floor of the number +** mode == 2: takes the ceil of the number +*/ +int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) { + TValue v; + again: + if (ttisfloat(obj)) { + lua_Number n = fltvalue(obj); + lua_Number f = l_floor(n); + if (n != f) { /* not an integral value? */ + if (mode == 0) return 0; /* fails if mode demands integral value */ + else if (mode > 1) /* needs ceil? */ + f += 1; /* convert floor to ceil (remember: n != f) */ + } + return lua_numbertointeger(f, p); } - if (counthook) - luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */ - if (mask & LUA_MASKLINE) { - Proto *p = ci_func(ci)->p; - int npc = pcRel(ci->u.l.savedpc, p); - int newline = getfuncline(p, npc); - if (npc == 0 || /* call linehook when enter a new function, */ - ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ - newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ - luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */ + else if (ttisinteger(obj)) { + *p = ivalue(obj); + return 1; } - L->oldpc = ci->u.l.savedpc; - if (L->status == LUA_YIELD) { /* did hook yield? */ - if (counthook) - L->hookcount = 1; /* undo decrement to zero */ - ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ - ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ - ci->func = L->top - 1; /* protect stack below results */ - luaD_throw(L, LUA_YIELD); + else if (cvt2num(obj) && + luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) { + obj = &v; + goto again; /* convert result from 'luaO_str2num' to an integer */ } + return 0; /* conversion failed */ } -static void callTM (lua_State *L, const TValue *f, const TValue *p1, - const TValue *p2, TValue *p3, int hasres) { - ptrdiff_t result = savestack(L, p3); - setobj2s(L, L->top++, f); /* push function */ - setobj2s(L, L->top++, p1); /* 1st argument */ - setobj2s(L, L->top++, p2); /* 2nd argument */ - if (!hasres) /* no result? 'p3' is third argument */ - setobj2s(L, L->top++, p3); /* 3rd argument */ - /* metamethod may yield only when called from Lua code */ - luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci)); - if (hasres) { /* if has result, move it to its place */ - p3 = restorestack(L, result); - setobjs2s(L, p3, --L->top); +/* +** Try to convert a 'for' limit to an integer, preserving the +** semantics of the loop. +** (The following explanation assumes a non-negative step; it is valid +** for negative steps mutatis mutandis.) +** If the limit can be converted to an integer, rounding down, that is +** it. +** Otherwise, check whether the limit can be converted to a number. If +** the number is too large, it is OK to set the limit as LUA_MAXINTEGER, +** which means no limit. If the number is too negative, the loop +** should not run, because any initial integer value is larger than the +** limit. So, it sets the limit to LUA_MININTEGER. 'stopnow' corrects +** the extreme case when the initial value is LUA_MININTEGER, in which +** case the LUA_MININTEGER limit would still run the loop once. +*/ +static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step, + int *stopnow) { + *stopnow = 0; /* usually, let loops run */ + if (!luaV_tointeger(obj, p, (step < 0 ? 2 : 1))) { /* not fit in integer? */ + lua_Number n; /* try to convert to float */ + if (!tonumber(obj, &n)) /* cannot convert to float? */ + return 0; /* not a number */ + if (luai_numlt(0, n)) { /* if true, float is larger than max integer */ + *p = LUA_MAXINTEGER; + if (step < 0) *stopnow = 1; + } + else { /* float is smaller than min integer */ + *p = LUA_MININTEGER; + if (step >= 0) *stopnow = 1; + } } + return 1; } -void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { - int loop; +/* +** Complete a table access: if 't' is a table, 'tm' has its metamethod; +** otherwise, 'tm' is NULL. +*/ +void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, + const TValue *tm) { + int loop; /* counter to avoid infinite loops */ + lua_assert(tm != NULL || !ttistable(t)); for (loop = 0; loop < MAXTAGLOOP; loop++) { - const TValue *tm; - if (ttistable(t)) { /* `t' is a table? */ - Table *h = hvalue(t); - const TValue *res = luaH_get(h, key); /* do a primitive get */ - if (!ttisnil(res) || /* result is not nil? */ - (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ - setobj2s(L, val, res); - return; - } - /* else will try the tag method */ + if (tm == NULL) { /* no metamethod (from a table)? */ + if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + luaG_typeerror(L, t, "index"); /* no metamethod */ } - else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) - luaG_typeerror(L, t, "index"); - if (ttisfunction(tm)) { - callTM(L, tm, t, key, val, 1); + if (ttisfunction(tm)) { /* metamethod is a function */ + luaT_callTM(L, tm, t, key, val, 1); /* call it */ + return; + } + t = tm; /* else repeat access over 'tm' */ + if (luaV_fastget(L,t,key,tm,luaH_get)) { /* try fast track */ + setobj2s(L, val, tm); /* done */ return; } - t = tm; /* else repeat with 'tm' */ + /* else repeat */ } - luaG_runerror(L, "loop in gettable"); + luaG_runerror(L, "gettable chain too long; possible loop"); } -void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { - int loop; +/* +** Main function for table assignment (invoking metamethods if needed). +** Compute 't[key] = val' +*/ +void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *oldval) { + int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; - if (ttistable(t)) { /* `t' is a table? */ - Table *h = hvalue(t); - TValue *oldval = cast(TValue *, luaH_get(h, key)); - /* if previous value is not nil, there must be a previous entry - in the table; moreover, a metamethod has no relevance */ - if (!ttisnil(oldval) || - /* previous value is nil; must check the metamethod */ - ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL && + if (oldval != NULL) { + lua_assert(ttistable(t) && ttisnil(oldval)); + /* must check the metamethod */ + if ((tm = fasttm(L, hvalue(t)->metatable, TM_NEWINDEX)) == NULL && /* no metamethod; is there a previous entry in the table? */ (oldval != luaO_nilobject || /* no previous entry; must create one. (The next test is always true; we only need the assignment.) */ - (oldval = luaH_newkey(L, h, key), 1)))) { + (oldval = luaH_newkey(L, hvalue(t), key), 1))) { /* no metamethod and (now) there is an entry with given key */ - setobj2t(L, oldval, val); /* assign new value to that entry */ - invalidateTMcache(h); - luaC_barrierback(L, obj2gco(h), val); + setobj2t(L, cast(TValue *, oldval), val); + invalidateTMcache(hvalue(t)); + luaC_barrierback(L, hvalue(t), val); return; } /* else will try the metamethod */ } - else /* not a table; check metamethod */ + else { /* not a table; check metamethod */ if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) luaG_typeerror(L, t, "index"); - /* there is a metamethod */ + } + /* try the metamethod */ if (ttisfunction(tm)) { - callTM(L, tm, t, key, val, 0); + luaT_callTM(L, tm, t, key, val, 0); return; } - t = tm; /* else repeat with 'tm' */ + t = tm; /* else repeat assignment over 'tm' */ + if (luaV_fastset(L, t, key, oldval, luaH_get, val)) + return; /* done */ + /* else loop */ } - luaG_runerror(L, "loop in settable"); + luaG_runerror(L, "settable chain too long; possible loop"); } -static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, - StkId res, TMS event) { - const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ - if (ttisnil(tm)) - tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ - if (ttisnil(tm)) return 0; - callTM(L, tm, p1, p2, res, 1); - return 1; +/* +** Compare two strings 'ls' x 'rs', returning an integer smaller-equal- +** -larger than zero if 'ls' is smaller-equal-larger than 'rs'. +** The code is a little tricky because it allows '\0' in the strings +** and it uses 'strcoll' (to respect locales) for each segments +** of the strings. +*/ +static int l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = tsslen(ls); + const char *r = getstr(rs); + size_t lr = tsslen(rs); + for (;;) { /* for each segment */ + int temp = strcoll(l, r); + if (temp != 0) /* not equal? */ + return temp; /* done */ + else { /* strings are equal up to a '\0' */ + size_t len = strlen(l); /* index of first '\0' in both strings */ + if (len == lr) /* 'rs' is finished? */ + return (len == ll) ? 0 : 1; /* check 'ls' */ + else if (len == ll) /* 'ls' is finished? */ + return -1; /* 'ls' is smaller than 'rs' ('rs' is not finished) */ + /* both strings longer than 'len'; go on comparing after the '\0' */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } } -static const TValue *get_equalTM (lua_State *L, Table *mt1, Table *mt2, - TMS event) { - const TValue *tm1 = fasttm(L, mt1, event); - const TValue *tm2; - if (tm1 == NULL) return NULL; /* no metamethod */ - if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ - tm2 = fasttm(L, mt2, event); - if (tm2 == NULL) return NULL; /* no metamethod */ - if (luaV_rawequalobj(tm1, tm2)) /* same metamethods? */ - return tm1; - return NULL; +/* +** Check whether integer 'i' is less than float 'f'. If 'i' has an +** exact representation as a float ('l_intfitsf'), compare numbers as +** floats. Otherwise, if 'f' is outside the range for integers, result +** is trivial. Otherwise, compare them as integers. (When 'i' has no +** float representation, either 'f' is "far away" from 'i' or 'f' has +** no precision left for a fractional part; either way, how 'f' is +** truncated is irrelevant.) When 'f' is NaN, comparisons must result +** in false. +*/ +static int LTintfloat (lua_Integer i, lua_Number f) { +#if defined(l_intfitsf) + if (!l_intfitsf(i)) { + if (f >= -cast_num(LUA_MININTEGER)) /* -minint == maxint + 1 */ + return 1; /* f >= maxint + 1 > i */ + else if (f > cast_num(LUA_MININTEGER)) /* minint < f <= maxint ? */ + return (i < cast(lua_Integer, f)); /* compare them as integers */ + else /* f <= minint <= i (or 'f' is NaN) --> not(i < f) */ + return 0; + } +#endif + return luai_numlt(cast_num(i), f); /* compare them as floats */ } -static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, - TMS event) { - if (!call_binTM(L, p1, p2, L->top, event)) - return -1; /* no metamethod */ - else - return !l_isfalse(L->top); +/* +** Check whether integer 'i' is less than or equal to float 'f'. +** See comments on previous function. +*/ +static int LEintfloat (lua_Integer i, lua_Number f) { +#if defined(l_intfitsf) + if (!l_intfitsf(i)) { + if (f >= -cast_num(LUA_MININTEGER)) /* -minint == maxint + 1 */ + return 1; /* f >= maxint + 1 > i */ + else if (f >= cast_num(LUA_MININTEGER)) /* minint <= f <= maxint ? */ + return (i <= cast(lua_Integer, f)); /* compare them as integers */ + else /* f < minint <= i (or 'f' is NaN) --> not(i <= f) */ + return 0; + } +#endif + return luai_numle(cast_num(i), f); /* compare them as floats */ } -static int l_strcmp (const TString *ls, const TString *rs) { - const char *l = getstr(ls); - size_t ll = ls->tsv.len; - const char *r = getstr(rs); - size_t lr = rs->tsv.len; - for (;;) { - int temp = strcoll(l, r); - if (temp != 0) return temp; - else { /* strings are equal up to a `\0' */ - size_t len = strlen(l); /* index of first `\0' in both strings */ - if (len == lr) /* r is finished? */ - return (len == ll) ? 0 : 1; - else if (len == ll) /* l is finished? */ - return -1; /* l is smaller than r (because r is not finished) */ - /* both strings longer than `len'; go on comparing (after the `\0') */ - len++; - l += len; ll -= len; r += len; lr -= len; - } +/* +** Return 'l < r', for numbers. +*/ +static int LTnum (const TValue *l, const TValue *r) { + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li < ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LTintfloat(li, fltvalue(r)); /* l < r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numlt(lf, fltvalue(r)); /* both are float */ + else if (luai_numisnan(lf)) /* 'r' is int and 'l' is float */ + return 0; /* NaN < i is always false */ + else /* without NaN, (l < r) <--> not(r <= l) */ + return !LEintfloat(ivalue(r), lf); /* not (r <= l) ? */ } } +/* +** Return 'l <= r', for numbers. +*/ +static int LEnum (const TValue *l, const TValue *r) { + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li <= ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LEintfloat(li, fltvalue(r)); /* l <= r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numle(lf, fltvalue(r)); /* both are float */ + else if (luai_numisnan(lf)) /* 'r' is int and 'l' is float */ + return 0; /* NaN <= i is always false */ + else /* without NaN, (l <= r) <--> not(r < l) */ + return !LTintfloat(ivalue(r), lf); /* not (r < l) ? */ + } +} + + +/* +** Main operation less than; return 'l < r'. +*/ int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { int res; - if (ttisnumber(l) && ttisnumber(r)) - return luai_numlt(L, nvalue(l), nvalue(r)); - else if (ttisstring(l) && ttisstring(r)) - return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; - else if ((res = call_orderTM(L, l, r, TM_LT)) < 0) - luaG_ordererror(L, l, r); + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LTnum(l, r); + else if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) < 0; + else if ((res = luaT_callorderTM(L, l, r, TM_LT)) < 0) /* no metamethod? */ + luaG_ordererror(L, l, r); /* error */ return res; } +/* +** Main operation less than or equal to; return 'l <= r'. If it needs +** a metamethod and there is no '__le', try '__lt', based on +** l <= r iff !(r < l) (assuming a total order). If the metamethod +** yields during this substitution, the continuation has to know +** about it (to negate the result of r= 0) /* first try `le' */ + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LEnum(l, r); + else if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) <= 0; + else if ((res = luaT_callorderTM(L, l, r, TM_LE)) >= 0) /* try 'le' */ return res; - else if ((res = call_orderTM(L, r, l, TM_LT)) < 0) /* else try `lt' */ - luaG_ordererror(L, l, r); - return !res; + else { /* try 'lt': */ + L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ + res = luaT_callorderTM(L, r, l, TM_LT); + L->ci->callstatus ^= CIST_LEQ; /* clear mark */ + if (res < 0) + luaG_ordererror(L, l, r); + return !res; /* result is negated */ + } } /* -** equality of Lua values. L == NULL means raw equality (no metamethods) +** Main operation for equality of Lua values; return 't1 == t2'. +** L == NULL means raw equality (no metamethods) */ -int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2) { +int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; - lua_assert(ttisequal(t1, t2)); + if (ttype(t1) != ttype(t2)) { /* not the same variant? */ + if (ttnov(t1) != ttnov(t2) || ttnov(t1) != LUA_TNUMBER) + return 0; /* only numbers can be equal with different variants */ + else { /* two numbers with different variants */ + lua_Integer i1, i2; /* compare them as integers */ + return (tointeger(t1, &i1) && tointeger(t2, &i2) && i1 == i2); + } + } + /* values have same type and same variant */ switch (ttype(t1)) { case LUA_TNIL: return 1; - case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TNUMINT: return (ivalue(t1) == ivalue(t2)); + case LUA_TNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); case LUA_TLCF: return fvalue(t1) == fvalue(t2); - case LUA_TSHRSTR: return eqshrstr(rawtsvalue(t1), rawtsvalue(t2)); - case LUA_TLNGSTR: return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2)); + case LUA_TSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); + case LUA_TLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); case LUA_TUSERDATA: { if (uvalue(t1) == uvalue(t2)) return 1; else if (L == NULL) return 0; - tm = get_equalTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ); + tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } case LUA_TTABLE: { if (hvalue(t1) == hvalue(t2)) return 1; else if (L == NULL) return 0; - tm = get_equalTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); + tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } default: - lua_assert(iscollectable(t1)); return gcvalue(t1) == gcvalue(t2); } - if (tm == NULL) return 0; /* no TM? */ - callTM(L, tm, t1, t2, L->top, 1); /* call TM */ + if (tm == NULL) /* no TM? */ + return 0; /* objects are different */ + luaT_callTM(L, tm, t1, t2, L->top, 1); /* call TM */ return !l_isfalse(L->top); } +/* macro used by 'luaV_concat' to ensure that element at 'o' is a string */ +#define tostring(L,o) \ + (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) + +#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) + +/* copy strings in stack from top - n up to top - 1 to buffer */ +static void copy2buff (StkId top, int n, char *buff) { + size_t tl = 0; /* size already copied */ + do { + size_t l = vslen(top - n); /* length of string being copied */ + memcpy(buff + tl, svalue(top - n), l * sizeof(char)); + tl += l; + } while (--n > 0); +} + + +/* +** Main operation for concatenation: concat 'total' values in the stack, +** from 'L->top - total' up to 'L->top - 1'. +*/ void luaV_concat (lua_State *L, int total) { lua_assert(total >= 2); do { StkId top = L->top; int n = 2; /* number of elements handled in this pass (at least 2) */ - if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { - if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) - luaG_concaterror(L, top-2, top-1); - } - else if (tsvalue(top-1)->len == 0) /* second operand is empty? */ - (void)tostring(L, top - 2); /* result is first operand */ - else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) { + if (!(ttisstring(top-2) || cvt2str(top-2)) || !tostring(L, top-1)) + luaT_trybinTM(L, top-2, top-1, top-2, TM_CONCAT); + else if (isemptystr(top - 1)) /* second operand is empty? */ + cast_void(tostring(L, top - 2)); /* result is first operand */ + else if (isemptystr(top - 2)) { /* first operand is an empty string? */ setobjs2s(L, top - 2, top - 1); /* result is second op. */ } else { /* at least two non-empty string values; get as many as possible */ - size_t tl = tsvalue(top-1)->len; - char *buffer; - int i; - /* collect total length */ - for (i = 1; i < total && tostring(L, top-i-1); i++) { - size_t l = tsvalue(top-i-1)->len; - if (l >= (MAX_SIZET/sizeof(char)) - tl) + size_t tl = vslen(top - 1); + TString *ts; + /* collect total length and number of strings */ + for (n = 1; n < total && tostring(L, top - n - 1); n++) { + size_t l = vslen(top - n - 1); + if (l >= (MAX_SIZE/sizeof(char)) - tl) luaG_runerror(L, "string length overflow"); tl += l; } - buffer = luaZ_openspace(L, &G(L)->buff, tl); - tl = 0; - n = i; - do { /* concat all strings */ - size_t l = tsvalue(top-i)->len; - memcpy(buffer+tl, svalue(top-i), l * sizeof(char)); - tl += l; - } while (--i > 0); - setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); + if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ + char buff[LUAI_MAXSHORTLEN]; + copy2buff(top, n, buff); /* copy strings to buffer */ + ts = luaS_newlstr(L, buff, tl); + } + else { /* long string; copy strings directly to final result */ + ts = luaS_createlngstrobj(L, tl); + copy2buff(top, n, getstr(ts)); + } + setsvalue2s(L, top - n, ts); /* create result */ } total -= n-1; /* got 'n' strings to create 1 new */ L->top -= n-1; /* popped 'n' strings and pushed one */ @@ -332,18 +497,25 @@ void luaV_concat (lua_State *L, int total) { } +/* +** Main operation 'ra' = #rb'. +*/ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { const TValue *tm; - switch (ttypenv(rb)) { + switch (ttype(rb)) { case LUA_TTABLE: { Table *h = hvalue(rb); tm = fasttm(L, h->metatable, TM_LEN); if (tm) break; /* metamethod? break switch to call it */ - setnvalue(ra, cast_num(luaH_getn(h))); /* else primitive len */ + setivalue(ra, luaH_getn(h)); /* else primitive len */ return; } - case LUA_TSTRING: { - setnvalue(ra, cast_num(tsvalue(rb)->len)); + case LUA_TSHRSTR: { + setivalue(ra, tsvalue(rb)->shrlen); + return; + } + case LUA_TLNGSTR: { + setivalue(ra, tsvalue(rb)->u.lnglen); return; } default: { /* try metamethod */ @@ -353,21 +525,66 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { break; } } - callTM(L, tm, rb, rb, ra, 1); + luaT_callTM(L, tm, rb, rb, ra, 1); +} + + +/* +** Integer division; return 'm // n', that is, floor(m/n). +** C division truncates its result (rounds towards zero). +** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, +** otherwise 'floor(q) == trunc(q) - 1'. +*/ +lua_Integer luaV_div (lua_State *L, lua_Integer m, lua_Integer n) { + if (l_castS2U(n) + 1u <= 1u) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to divide by zero"); + return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ + } + else { + lua_Integer q = m / n; /* perform C division */ + if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ + q -= 1; /* correct result for different rounding */ + return q; + } +} + + +/* +** Integer modulus; return 'm % n'. (Assume that C '%' with +** negative operands follows C99 behavior. See previous comment +** about luaV_div.) +*/ +lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { + if (l_castS2U(n) + 1u <= 1u) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to perform 'n%%0'"); + return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ + } + else { + lua_Integer r = m % n; + if (r != 0 && (m ^ n) < 0) /* 'm/n' would be non-integer negative? */ + r += n; /* correct result for different rounding */ + return r; + } } -void luaV_arith (lua_State *L, StkId ra, const TValue *rb, - const TValue *rc, TMS op) { - TValue tempb, tempc; - const TValue *b, *c; - if ((b = luaV_tonumber(rb, &tempb)) != NULL && - (c = luaV_tonumber(rc, &tempc)) != NULL) { - lua_Number res = luaO_arith(op - TM_ADD + LUA_OPADD, nvalue(b), nvalue(c)); - setnvalue(ra, res); +/* number of bits in an integer */ +#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) + +/* +** Shift left operation. (Shift right just negates 'y'.) +*/ +lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { + if (y < 0) { /* shift right? */ + if (y <= -NBITS) return 0; + else return intop(>>, x, -y); + } + else { /* shift left */ + if (y >= NBITS) return 0; + else return intop(<<, x, y); } - else if (!call_binTM(L, rb, rc, ra, op)) - luaG_aritherror(L, rb, rc); } @@ -376,15 +593,15 @@ void luaV_arith (lua_State *L, StkId ra, const TValue *rb, ** whether there is a cached closure with the same upvalues needed by ** new closure to be created. */ -static Closure *getcached (Proto *p, UpVal **encup, StkId base) { - Closure *c = p->cache; +static LClosure *getcached (Proto *p, UpVal **encup, StkId base) { + LClosure *c = p->cache; if (c != NULL) { /* is there a cached closure? */ int nup = p->sp->sizeupvalues; Upvaldesc *uv = p->sp->upvalues; int i; for (i = 0; i < nup; i++) { /* check whether it has right upvalues */ TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v; - if (c->l.upvals[i]->v != v) + if (c->upvals[i]->v != v) return NULL; /* wrong upvalue; cannot reuse closure */ } } @@ -394,26 +611,28 @@ static Closure *getcached (Proto *p, UpVal **encup, StkId base) { /* ** create a new Lua closure, push it in the stack, and initialize -** its upvalues. Note that the call to 'luaC_barrierproto' must come -** before the assignment to 'p->cache', as the function needs the -** original value of that field. +** its upvalues. Note that the closure is not cached if prototype is +** already black (which means that 'cache' was already cleared by the +** GC). */ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, StkId ra) { int nup = p->sp->sizeupvalues; Upvaldesc *uv = p->sp->upvalues; int i; - Closure *ncl = luaF_newLclosure(L, nup); - ncl->l.p = p; + LClosure *ncl = luaF_newLclosure(L, nup); + ncl->p = p; setclLvalue(L, ra, ncl); /* anchor new closure in stack */ for (i = 0; i < nup; i++) { /* fill in its upvalues */ if (uv[i].instack) /* upvalue refers to local variable? */ - ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx); + ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); else /* get upvalue from enclosing function */ - ncl->l.upvals[i] = encup[uv[i].idx]; + ncl->upvals[i] = encup[uv[i].idx]; + ncl->upvals[i]->refcount++; + /* new closure is white, so we do not need a barrier here */ } - luaC_barrierproto(L, p, ncl); - p->cache = ncl; /* save it on cache for reuse */ + if (!isblack(p)) /* cache will not break GC invariant? */ + p->cache = ncl; /* save it on cache for reuse */ } @@ -426,8 +645,10 @@ void luaV_finishOp (lua_State *L) { Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ OpCode op = GET_OPCODE(inst); switch (op) { /* finish its execution */ - case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: - case OP_MOD: case OP_POW: case OP_UNM: case OP_LEN: + case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_IDIV: + case OP_BAND: case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: + case OP_MOD: case OP_POW: + case OP_UNM: case OP_BNOT: case OP_LEN: case OP_GETTABUP: case OP_GETTABLE: case OP_SELF: { setobjs2s(L, base + GETARG_A(inst), --L->top); break; @@ -435,18 +656,18 @@ void luaV_finishOp (lua_State *L) { case OP_LE: case OP_LT: case OP_EQ: { int res = !l_isfalse(L->top - 1); L->top--; - /* metamethod should not be called when operand is K */ - lua_assert(!ISK(GETARG_B(inst))); - if (op == OP_LE && /* "<=" using "<" instead? */ - ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE))) - res = !res; /* invert result */ + if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ + lua_assert(op == OP_LE); + ci->callstatus ^= CIST_LEQ; /* clear mark */ + res = !res; /* negate result */ + } lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); if (res != GETARG_A(inst)) /* condition failed? */ ci->u.l.savedpc++; /* skip jump instruction */ break; } case OP_CONCAT: { - StkId top = L->top - 1; /* top when 'call_binTM' was called */ + StkId top = L->top - 1; /* top when 'luaT_trybinTM' was called */ int b = GETARG_B(inst); /* first element to concatenate */ int total = cast_int(top - 1 - (base + b)); /* yet to concatenate */ setobj2s(L, top - 2, top); /* put TM result in proper position */ @@ -477,31 +698,32 @@ void luaV_finishOp (lua_State *L) { + /* -** some macros for common tasks in `luaV_execute' +** {================================================================== +** Function 'luaV_execute': main interpreter loop +** =================================================================== */ -#if !defined luai_runtimecheck -#define luai_runtimecheck(L, c) /* void */ -#endif + +/* +** some macros for common tasks in 'luaV_execute' +*/ #define RA(i) (base+GETARG_A(i)) -/* to be used after possible stack reallocation */ #define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) #define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) #define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) #define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) -#define KBx(i) \ - (k + (GETARG_Bx(i) != 0 ? GETARG_Bx(i) - 1 : GETARG_Ax(*ci->u.l.savedpc++))) /* execute a jump instruction */ #define dojump(ci,i,e) \ { int a = GETARG_A(i); \ - if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \ + if (a != 0) luaF_close(L, ci->u.l.base + a - 1); \ ci->u.l.savedpc += GETARG_sBx(i) + e; } /* for test instructions, execute the jump instruction that follows it */ @@ -511,96 +733,119 @@ void luaV_finishOp (lua_State *L) { #define Protect(x) { {x;}; base = ci->u.l.base; } #define checkGC(L,c) \ - Protect( luaC_condGC(L,{L->top = (c); /* limit of live values */ \ - luaC_step(L); \ - L->top = ci->top;}) /* restore top */ \ - luai_threadyield(L); ) + { luaC_condGC(L, L->top = (c), /* limit of live values */ \ + Protect(L->top = ci->top)); /* restore top */ \ + luai_threadyield(L); } -#define arith_op(op,tm) { \ - TValue *rb = RKB(i); \ - TValue *rc = RKC(i); \ - if (ttisnumber(rb) && ttisnumber(rc)) { \ - lua_Number nb = nvalue(rb), nc = nvalue(rc); \ - setnvalue(ra, op(L, nb, nc)); \ - } \ - else { Protect(luaV_arith(L, ra, rb, rc, tm)); } } +#define vmdispatch(o) switch(o) +#define vmcase(l) case l: +#define vmbreak break + + +/* +** copy of 'luaV_gettable', but protecting call to potential metamethod +** (which can reallocate the stack) +*/ +#define gettableProtected(L,t,k,v) { const TValue *aux; \ + if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ + else Protect(luaV_finishget(L,t,k,v,aux)); } + + +/* same for 'luaV_settable' */ +#define settableProtected(L,t,k,v) { const TValue *slot; \ + if (!luaV_fastset(L,t,k,slot,luaH_get,v)) \ + Protect(luaV_finishset(L,t,k,v,slot)); } -#define vmdispatch(o) switch(o) -#define vmcase(l,b) case l: {b} break; -#define vmcasenb(l,b) case l: {b} /* nb = no break */ void luaV_execute (lua_State *L) { CallInfo *ci = L->ci; LClosure *cl; TValue *k; StkId base; + ci->callstatus |= CIST_FRESH; /* fresh invocation of 'luaV_execute" */ newframe: /* reentry point when frame changes (call/return) */ lua_assert(ci == L->ci); - cl = clLvalue(ci->func); - k = cl->p->k; - base = ci->u.l.base; + cl = clLvalue(ci->func); /* local reference to function's closure */ + k = cl->p->k; /* local reference to function's constant table */ + base = ci->u.l.base; /* local copy of function's base */ /* main loop of interpreter */ for (;;) { Instruction i = *(ci->u.l.savedpc++); StkId ra; - if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && - (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { - Protect(traceexec(L)); - } - /* WARNING: several calls may realloc the stack and invalidate `ra' */ + if (L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) + Protect(luaG_traceexec(L)); + /* WARNING: several calls may realloc the stack and invalidate 'ra' */ ra = RA(i); lua_assert(base == ci->u.l.base); lua_assert(base <= L->top && L->top < L->stack + L->stacksize); vmdispatch (GET_OPCODE(i)) { - vmcase(OP_MOVE, + vmcase(OP_MOVE) { setobjs2s(L, ra, RB(i)); - ) - vmcase(OP_LOADK, + vmbreak; + } + vmcase(OP_LOADK) { TValue *rb = k + GETARG_Bx(i); setobj2s(L, ra, rb); - ) - vmcase(OP_LOADKX, + vmbreak; + } + vmcase(OP_LOADKX) { TValue *rb; lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); rb = k + GETARG_Ax(*ci->u.l.savedpc++); setobj2s(L, ra, rb); - ) - vmcase(OP_LOADBOOL, + vmbreak; + } + vmcase(OP_LOADBOOL) { setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) ci->u.l.savedpc++; /* skip next instruction (if C) */ - ) - vmcase(OP_LOADNIL, + vmbreak; + } + vmcase(OP_LOADNIL) { int b = GETARG_B(i); do { setnilvalue(ra++); } while (b--); - ) - vmcase(OP_GETUPVAL, + vmbreak; + } + vmcase(OP_GETUPVAL) { int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); - ) - vmcase(OP_GETTABUP, - int b = GETARG_B(i); - Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra)); - ) - vmcase(OP_GETTABLE, - Protect(luaV_gettable(L, RB(i), RKC(i), ra)); - ) - vmcase(OP_SETTABUP, - int a = GETARG_A(i); - Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i))); - ) - vmcase(OP_SETUPVAL, + vmbreak; + } + vmcase(OP_GETTABUP) { + TValue *upval = cl->upvals[GETARG_B(i)]->v; + TValue *rc = RKC(i); + gettableProtected(L, upval, rc, ra); + vmbreak; + } + vmcase(OP_GETTABLE) { + StkId rb = RB(i); + TValue *rc = RKC(i); + gettableProtected(L, rb, rc, ra); + vmbreak; + } + vmcase(OP_SETTABUP) { + TValue *upval = cl->upvals[GETARG_A(i)]->v; + TValue *rb = RKB(i); + TValue *rc = RKC(i); + settableProtected(L, upval, rb, rc); + vmbreak; + } + vmcase(OP_SETUPVAL) { UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); - luaC_barrier(L, uv, ra); - ) - vmcase(OP_SETTABLE, - Protect(luaV_settable(L, ra, RKB(i), RKC(i))); - ) - vmcase(OP_NEWTABLE, + luaC_upvalbarrier(L, uv); + vmbreak; + } + vmcase(OP_SETTABLE) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + settableProtected(L, ra, rb, rc); + vmbreak; + } + vmcase(OP_NEWTABLE) { int b = GETARG_B(i); int c = GETARG_C(i); Table *t = luaH_new(L); @@ -608,96 +853,252 @@ void luaV_execute (lua_State *L) { if (b != 0 || c != 0) luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); checkGC(L, ra + 1); - ) - vmcase(OP_SELF, + vmbreak; + } + vmcase(OP_SELF) { + const TValue *aux; StkId rb = RB(i); - setobjs2s(L, ra+1, rb); - Protect(luaV_gettable(L, rb, RKC(i), ra)); - ) - vmcase(OP_ADD, - arith_op(luai_numadd, TM_ADD); - ) - vmcase(OP_SUB, - arith_op(luai_numsub, TM_SUB); - ) - vmcase(OP_MUL, - arith_op(luai_nummul, TM_MUL); - ) - vmcase(OP_DIV, - arith_op(luai_numdiv, TM_DIV); - ) - vmcase(OP_MOD, - arith_op(luai_nummod, TM_MOD); - ) - vmcase(OP_POW, - arith_op(luai_numpow, TM_POW); - ) - vmcase(OP_UNM, + TValue *rc = RKC(i); + TString *key = tsvalue(rc); /* key must be a string */ + setobjs2s(L, ra + 1, rb); + if (luaV_fastget(L, rb, key, aux, luaH_getstr)) { + setobj2s(L, ra, aux); + } + else Protect(luaV_finishget(L, rb, rc, ra, aux)); + vmbreak; + } + vmcase(OP_ADD) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (ttisinteger(rb) && ttisinteger(rc)) { + lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); + setivalue(ra, intop(+, ib, ic)); + } + else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + setfltvalue(ra, luai_numadd(L, nb, nc)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_ADD)); } + vmbreak; + } + vmcase(OP_SUB) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (ttisinteger(rb) && ttisinteger(rc)) { + lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); + setivalue(ra, intop(-, ib, ic)); + } + else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + setfltvalue(ra, luai_numsub(L, nb, nc)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SUB)); } + vmbreak; + } + vmcase(OP_MUL) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (ttisinteger(rb) && ttisinteger(rc)) { + lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); + setivalue(ra, intop(*, ib, ic)); + } + else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + setfltvalue(ra, luai_nummul(L, nb, nc)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_MUL)); } + vmbreak; + } + vmcase(OP_DIV) { /* float division (always with floats) */ + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + setfltvalue(ra, luai_numdiv(L, nb, nc)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_DIV)); } + vmbreak; + } + vmcase(OP_BAND) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Integer ib; lua_Integer ic; + if (tointeger(rb, &ib) && tointeger(rc, &ic)) { + setivalue(ra, intop(&, ib, ic)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BAND)); } + vmbreak; + } + vmcase(OP_BOR) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Integer ib; lua_Integer ic; + if (tointeger(rb, &ib) && tointeger(rc, &ic)) { + setivalue(ra, intop(|, ib, ic)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BOR)); } + vmbreak; + } + vmcase(OP_BXOR) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Integer ib; lua_Integer ic; + if (tointeger(rb, &ib) && tointeger(rc, &ic)) { + setivalue(ra, intop(^, ib, ic)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BXOR)); } + vmbreak; + } + vmcase(OP_SHL) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Integer ib; lua_Integer ic; + if (tointeger(rb, &ib) && tointeger(rc, &ic)) { + setivalue(ra, luaV_shiftl(ib, ic)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHL)); } + vmbreak; + } + vmcase(OP_SHR) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Integer ib; lua_Integer ic; + if (tointeger(rb, &ib) && tointeger(rc, &ic)) { + setivalue(ra, luaV_shiftl(ib, -ic)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHR)); } + vmbreak; + } + vmcase(OP_MOD) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (ttisinteger(rb) && ttisinteger(rc)) { + lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); + setivalue(ra, luaV_mod(L, ib, ic)); + } + else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + lua_Number m; + luai_nummod(L, nb, nc, m); + setfltvalue(ra, m); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_MOD)); } + vmbreak; + } + vmcase(OP_IDIV) { /* floor division */ + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (ttisinteger(rb) && ttisinteger(rc)) { + lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); + setivalue(ra, luaV_div(L, ib, ic)); + } + else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + setfltvalue(ra, luai_numidiv(L, nb, nc)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_IDIV)); } + vmbreak; + } + vmcase(OP_POW) { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + setfltvalue(ra, luai_numpow(L, nb, nc)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_POW)); } + vmbreak; + } + vmcase(OP_UNM) { TValue *rb = RB(i); - if (ttisnumber(rb)) { - lua_Number nb = nvalue(rb); - setnvalue(ra, luai_numunm(L, nb)); + lua_Number nb; + if (ttisinteger(rb)) { + lua_Integer ib = ivalue(rb); + setivalue(ra, intop(-, 0, ib)); + } + else if (tonumber(rb, &nb)) { + setfltvalue(ra, luai_numunm(L, nb)); } else { - Protect(luaV_arith(L, ra, rb, rb, TM_UNM)); + Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); } - ) - vmcase(OP_NOT, + vmbreak; + } + vmcase(OP_BNOT) { + TValue *rb = RB(i); + lua_Integer ib; + if (tointeger(rb, &ib)) { + setivalue(ra, intop(^, ~l_castS2U(0), ib)); + } + else { + Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT)); + } + vmbreak; + } + vmcase(OP_NOT) { TValue *rb = RB(i); int res = l_isfalse(rb); /* next assignment may change this value */ setbvalue(ra, res); - ) - vmcase(OP_LEN, + vmbreak; + } + vmcase(OP_LEN) { Protect(luaV_objlen(L, ra, RB(i))); - ) - vmcase(OP_CONCAT, + vmbreak; + } + vmcase(OP_CONCAT) { int b = GETARG_B(i); int c = GETARG_C(i); StkId rb; L->top = base + c + 1; /* mark the end of concat operands */ Protect(luaV_concat(L, c - b + 1)); - ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */ - rb = b + base; + ra = RA(i); /* 'luaV_concat' may invoke TMs and move the stack */ + rb = base + b; setobjs2s(L, ra, rb); checkGC(L, (ra >= rb ? ra + 1 : rb)); L->top = ci->top; /* restore top */ - ) - vmcase(OP_JMP, + vmbreak; + } + vmcase(OP_JMP) { dojump(ci, i, 0); - ) - vmcase(OP_EQ, + vmbreak; + } + vmcase(OP_EQ) { TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( - if (cast_int(equalobj(L, rb, rc)) != GETARG_A(i)) + if (luaV_equalobj(L, rb, rc) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) - ) - vmcase(OP_LT, + vmbreak; + } + vmcase(OP_LT) { Protect( if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) - ) - vmcase(OP_LE, + vmbreak; + } + vmcase(OP_LE) { Protect( if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) - ) - vmcase(OP_TEST, + vmbreak; + } + vmcase(OP_TEST) { if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra)) ci->u.l.savedpc++; else donextjump(ci); - ) - vmcase(OP_TESTSET, + vmbreak; + } + vmcase(OP_TESTSET) { TValue *rb = RB(i); if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb)) ci->u.l.savedpc++; @@ -705,27 +1106,30 @@ void luaV_execute (lua_State *L) { setobjs2s(L, ra, rb); donextjump(ci); } - ) - vmcase(OP_CALL, + vmbreak; + } + vmcase(OP_CALL) { int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra+b; /* else previous instruction set top */ if (luaD_precall(L, ra, nresults)) { /* C function? */ - if (nresults >= 0) L->top = ci->top; /* adjust results */ - base = ci->u.l.base; + if (nresults >= 0) + L->top = ci->top; /* adjust results */ + Protect((void)0); /* update 'base' */ } else { /* Lua function */ ci = L->ci; - ci->callstatus |= CIST_REENTRY; goto newframe; /* restart luaV_execute over new Lua function */ } - ) - vmcase(OP_TAILCALL, + vmbreak; + } + vmcase(OP_TAILCALL) { int b = GETARG_B(i); if (b != 0) L->top = ra+b; /* else previous instruction set top */ lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); - if (luaD_precall(L, ra, LUA_MULTRET)) /* C function? */ - base = ci->u.l.base; + if (luaD_precall(L, ra, LUA_MULTRET)) { /* C function? */ + Protect((void)0); /* update 'base' */ + } else { /* tail call: put called frame (n) in place of caller one (o) */ CallInfo *nci = L->ci; /* called frame */ @@ -748,13 +1152,13 @@ void luaV_execute (lua_State *L) { lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize); goto newframe; /* restart luaV_execute over new Lua function */ } - ) - vmcasenb(OP_RETURN, + vmbreak; + } + vmcase(OP_RETURN) { int b = GETARG_B(i); - if (b != 0) L->top = ra+b-1; if (cl->p->sp->sizep > 0) luaF_close(L, base); - b = luaD_poscall(L, ra); - if (!(ci->callstatus & CIST_REENTRY)) /* 'ci' still the called one */ + b = luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra))); + if (ci->callstatus & CIST_FRESH) /* local 'ci' still from callee */ return; /* external invocation: return */ else { /* invocation via reentry: continue execution */ ci = L->ci; @@ -763,105 +1167,137 @@ void luaV_execute (lua_State *L) { lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL); goto newframe; /* restart luaV_execute over new Lua function */ } - ) - vmcase(OP_FORLOOP, - lua_Number step = nvalue(ra+2); - lua_Number idx = luai_numadd(L, nvalue(ra), step); /* increment index */ - lua_Number limit = nvalue(ra+1); - if (luai_numlt(L, 0, step) ? luai_numle(L, idx, limit) - : luai_numle(L, limit, idx)) { - ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ - setnvalue(ra, idx); /* update internal index... */ - setnvalue(ra+3, idx); /* ...and external index */ - } - ) - vmcase(OP_FORPREP, - const TValue *init = ra; - const TValue *plimit = ra+1; - const TValue *pstep = ra+2; - if (!tonumber(init, ra)) - luaG_runerror(L, LUA_QL("for") " initial value must be a number"); - else if (!tonumber(plimit, ra+1)) - luaG_runerror(L, LUA_QL("for") " limit must be a number"); - else if (!tonumber(pstep, ra+2)) - luaG_runerror(L, LUA_QL("for") " step must be a number"); - setnvalue(ra, luai_numsub(L, nvalue(ra), nvalue(pstep))); + } + vmcase(OP_FORLOOP) { + if (ttisinteger(ra)) { /* integer loop? */ + lua_Integer step = ivalue(ra + 2); + lua_Integer idx = intop(+, ivalue(ra), step); /* increment index */ + lua_Integer limit = ivalue(ra + 1); + if ((0 < step) ? (idx <= limit) : (limit <= idx)) { + ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ + chgivalue(ra, idx); /* update internal index... */ + setivalue(ra + 3, idx); /* ...and external index */ + } + } + else { /* floating loop */ + lua_Number step = fltvalue(ra + 2); + lua_Number idx = luai_numadd(L, fltvalue(ra), step); /* inc. index */ + lua_Number limit = fltvalue(ra + 1); + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ + chgfltvalue(ra, idx); /* update internal index... */ + setfltvalue(ra + 3, idx); /* ...and external index */ + } + } + vmbreak; + } + vmcase(OP_FORPREP) { + TValue *init = ra; + TValue *plimit = ra + 1; + TValue *pstep = ra + 2; + lua_Integer ilimit; + int stopnow; + if (ttisinteger(init) && ttisinteger(pstep) && + forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) { + /* all values are integer */ + lua_Integer initv = (stopnow ? 0 : ivalue(init)); + setivalue(plimit, ilimit); + setivalue(init, intop(-, initv, ivalue(pstep))); + } + else { /* try making all values floats */ + lua_Number ninit; lua_Number nlimit; lua_Number nstep; + if (!tonumber(plimit, &nlimit)) + luaG_runerror(L, "'for' limit must be a number"); + setfltvalue(plimit, nlimit); + if (!tonumber(pstep, &nstep)) + luaG_runerror(L, "'for' step must be a number"); + setfltvalue(pstep, nstep); + if (!tonumber(init, &ninit)) + luaG_runerror(L, "'for' initial value must be a number"); + setfltvalue(init, luai_numsub(L, ninit, nstep)); + } ci->u.l.savedpc += GETARG_sBx(i); - ) - vmcasenb(OP_TFORCALL, + vmbreak; + } + vmcase(OP_TFORCALL) { StkId cb = ra + 3; /* call base */ setobjs2s(L, cb+2, ra+2); setobjs2s(L, cb+1, ra+1); setobjs2s(L, cb, ra); L->top = cb + 3; /* func. + 2 args (state and index) */ - Protect(luaD_call(L, cb, GETARG_C(i), 1)); + Protect(luaD_call(L, cb, GETARG_C(i))); L->top = ci->top; i = *(ci->u.l.savedpc++); /* go to next instruction */ ra = RA(i); lua_assert(GET_OPCODE(i) == OP_TFORLOOP); goto l_tforloop; - ) - vmcase(OP_TFORLOOP, + } + vmcase(OP_TFORLOOP) { l_tforloop: if (!ttisnil(ra + 1)) { /* continue loop? */ setobjs2s(L, ra, ra + 1); /* save control variable */ ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ } - ) - vmcase(OP_SETLIST, + vmbreak; + } + vmcase(OP_SETLIST) { int n = GETARG_B(i); int c = GETARG_C(i); - int last; + unsigned int last; Table *h; if (n == 0) n = cast_int(L->top - ra) - 1; if (c == 0) { lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); c = GETARG_Ax(*ci->u.l.savedpc++); } - luai_runtimecheck(L, ttistable(ra)); h = hvalue(ra); last = ((c-1)*LFIELDS_PER_FLUSH) + n; if (last > h->sizearray) /* needs more space? */ - luaH_resizearray(L, h, last); /* pre-allocate it at once */ + luaH_resizearray(L, h, last); /* preallocate it at once */ for (; n > 0; n--) { TValue *val = ra+n; luaH_setint(L, h, last--, val); - luaC_barrierback(L, obj2gco(h), val); + luaC_barrierback(L, h, val); } L->top = ci->top; /* correct top (in case of previous open call) */ - ) - vmcase(OP_CLOSURE, + vmbreak; + } + vmcase(OP_CLOSURE) { Proto *p = cl->p->p[GETARG_Bx(i)]; - Closure *ncl = getcached(p, cl->upvals, base); /* cached closure */ + LClosure *ncl = getcached(p, cl->upvals, base); /* cached closure */ if (ncl == NULL) /* no match? */ pushclosure(L, p, cl->upvals, base, ra); /* create a new one */ else setclLvalue(L, ra, ncl); /* push cashed closure */ checkGC(L, ra + 1); - ) - vmcase(OP_VARARG, - int b = GETARG_B(i) - 1; + vmbreak; + } + vmcase(OP_VARARG) { + int b = GETARG_B(i) - 1; /* required results */ int j; int n = cast_int(base - ci->func) - cl->p->sp->numparams - 1; + if (n < 0) /* less arguments than parameters? */ + n = 0; /* no vararg arguments */ if (b < 0) { /* B == 0? */ b = n; /* get all var. arguments */ Protect(luaD_checkstack(L, n)); ra = RA(i); /* previous call may change the stack */ L->top = ra + n; } - for (j = 0; j < b; j++) { - if (j < n) { - setobjs2s(L, ra + j, base - n + j); - } - else { - setnilvalue(ra + j); - } - } - ) - vmcase(OP_EXTRAARG, + for (j = 0; j < b && j < n; j++) + setobjs2s(L, ra + j, base - n + j); + for (; j < b; j++) /* complete required results with nil */ + setnilvalue(ra + j); + vmbreak; + } + vmcase(OP_EXTRAARG) { lua_assert(0); - ) + vmbreak; + } } } } +/* }================================================================== */ + diff --git a/deps/lua/lvm.h b/deps/lua/lvm.h index 5380270..fd0e748 100644 --- a/deps/lua/lvm.h +++ b/deps/lua/lvm.h @@ -1,5 +1,5 @@ /* -** $Id: lvm.h,v 2.18.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lvm.h,v 2.39 2015/09/09 13:44:07 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -13,32 +13,102 @@ #include "ltm.h" -#define tostring(L,o) (ttisstring(o) || (luaV_tostring(L, o))) +#if !defined(LUA_NOCVTN2S) +#define cvt2str(o) ttisnumber(o) +#else +#define cvt2str(o) 0 /* no conversion from numbers to strings */ +#endif + + +#if !defined(LUA_NOCVTS2N) +#define cvt2num(o) ttisstring(o) +#else +#define cvt2num(o) 0 /* no conversion from strings to numbers */ +#endif + + +/* +** You can define LUA_FLOORN2I if you want to convert floats to integers +** by flooring them (instead of raising an error if they are not +** integral values) +*/ +#if !defined(LUA_FLOORN2I) +#define LUA_FLOORN2I 0 +#endif + + +#define tonumber(o,n) \ + (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) -#define tonumber(o,n) (ttisnumber(o) || (((o) = luaV_tonumber(o,n)) != NULL)) +#define tointeger(o,i) \ + (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) -#define equalobj(L,o1,o2) (ttisequal(o1, o2) && luaV_equalobj_(L, o1, o2)) +#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) -#define luaV_rawequalobj(o1,o2) equalobj(NULL,o1,o2) +#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) + + +/* +** fast track for 'gettable': 1 means 'aux' points to resulted value; +** 0 means 'aux' is metamethod (if 't' is a table) or NULL. 'f' is +** the raw get function to use. +*/ +#define luaV_fastget(L,t,k,aux,f) \ + (!ttistable(t) \ + ? (aux = NULL, 0) /* not a table; 'aux' is NULL and result is 0 */ \ + : (aux = f(hvalue(t), k), /* else, do raw access */ \ + !ttisnil(aux) ? 1 /* result not nil? 'aux' has it */ \ + : (aux = fasttm(L, hvalue(t)->metatable, TM_INDEX), /* get metamethod */\ + aux != NULL ? 0 /* has metamethod? must call it */ \ + : (aux = luaO_nilobject, 1)))) /* else, final result is nil */ + +/* +** standard implementation for 'gettable' +*/ +#define luaV_gettable(L,t,k,v) { const TValue *aux; \ + if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ + else luaV_finishget(L,t,k,v,aux); } + + +/* +** Fast track for set table. If 't' is a table and 't[k]' is not nil, +** call GC barrier, do a raw 't[k]=v', and return true; otherwise, +** return false with 'slot' equal to NULL (if 't' is not a table) or +** 'nil'. (This is needed by 'luaV_finishget'.) Note that, if the macro +** returns true, there is no need to 'invalidateTMcache', because the +** call is not creating a new entry. +*/ +#define luaV_fastset(L,t,k,slot,f,v) \ + (!ttistable(t) \ + ? (slot = NULL, 0) \ + : (slot = f(hvalue(t), k), \ + ttisnil(slot) ? 0 \ + : (luaC_barrierback(L, hvalue(t), v), \ + setobj2t(L, cast(TValue *,slot), v), \ + 1))) -/* not to called directly */ -LUAI_FUNC int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2); +#define luaV_settable(L,t,k,v) { const TValue *slot; \ + if (!luaV_fastset(L,t,k,slot,luaH_get,v)) \ + luaV_finishset(L,t,k,v,slot); } + +LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); -LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); -LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); -LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, - StkId val); -LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, - StkId val); +LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); +LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode); +LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *tm); +LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *oldval); LUAI_FUNC void luaV_finishOp (lua_State *L); LUAI_FUNC void luaV_execute (lua_State *L); LUAI_FUNC void luaV_concat (lua_State *L, int total); -LUAI_FUNC void luaV_arith (lua_State *L, StkId ra, const TValue *rb, - const TValue *rc, TMS op); +LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); #endif diff --git a/deps/lua/lzio.c b/deps/lua/lzio.c index 20efea9..c9e1f49 100644 --- a/deps/lua/lzio.c +++ b/deps/lua/lzio.c @@ -1,15 +1,17 @@ /* -** $Id: lzio.c,v 1.35.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lzio.c,v 1.37 2015/09/08 15:41:05 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ - -#include - #define lzio_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "llimits.h" @@ -64,13 +66,3 @@ size_t luaZ_read (ZIO *z, void *b, size_t n) { return 0; } -/* ------------------------------------------------------------------------ */ -char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - luaZ_resizebuffer(L, buff, n); - } - return buff->buffer; -} - - diff --git a/deps/lua/lzio.h b/deps/lua/lzio.h index 441f747..e7b6f34 100644 --- a/deps/lua/lzio.h +++ b/deps/lua/lzio.h @@ -1,5 +1,5 @@ /* -** $Id: lzio.h,v 1.26.1.1 2013/04/12 18:48:47 roberto Exp $ +** $Id: lzio.h,v 1.31 2015/09/08 15:41:05 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ @@ -32,20 +32,21 @@ typedef struct Mbuffer { #define luaZ_sizebuffer(buff) ((buff)->buffsize) #define luaZ_bufflen(buff) ((buff)->n) +#define luaZ_buffremove(buff,i) ((buff)->n -= (i)) #define luaZ_resetbuffer(buff) ((buff)->n = 0) #define luaZ_resizebuffer(L, buff, size) \ - (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ + ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ + (buff)->buffsize, size), \ (buff)->buffsize = size) #define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) -LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data); -LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ +LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ @@ -55,7 +56,7 @@ struct Zio { size_t n; /* bytes still unread */ const char *p; /* current position in buffer */ lua_Reader reader; /* reader function */ - void* data; /* additional data */ + void *data; /* additional data */ lua_State *L; /* Lua state (for reader) */ }; diff --git a/deps/uv/config-unix.mk b/deps/uv/config-unix.mk index d230bb2..5b66efb 100644 --- a/deps/uv/config-unix.mk +++ b/deps/uv/config-unix.mk @@ -107,7 +107,8 @@ OBJS += src/unix/linux-core.o \ endif ifeq (freebsd,$(PLATFORM)) -ifeq ($(shell dtrace -l 1>&2 2>/dev/null; echo $$?),0) +# ifeq ($(shell dtrace -l 1>&2 2>/dev/null; echo $$?),0) +ifeq ($(shell dtrace -l >/dev/null; echo $$?),0) #modified by xdczju@sina.com HAVE_DTRACE ?= 1 endif LDFLAGS+=-lkvm diff --git a/deps/uv/libuv.a b/deps/uv/libuv.a deleted file mode 100644 index 0c50212..0000000 Binary files a/deps/uv/libuv.a and /dev/null differ diff --git a/deps/uv/uv.vcxproj b/deps/uv/uv.vcxproj index c829a57..b3096da 100644 --- a/deps/uv/uv.vcxproj +++ b/deps/uv/uv.vcxproj @@ -69,11 +69,11 @@ Unicode - Application + StaticLibrary false v120 true - MultiByte + Unicode @@ -88,13 +88,17 @@ $(ProjectDir) + + $(ProjectDir) + .lib + Level3 Disabled false include;include\uv-private; - WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + _DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) @@ -111,7 +115,9 @@ MaxSpeed true true - true + false + include;include\uv-private; + _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) true diff --git a/install.bat b/install.bat deleted file mode 100644 index d31c319..0000000 --- a/install.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -REM lua dll is essential but not mime -if /i "%~2" == "lua" (copy "%~1\deps\lua\nlua.dll" "%~1" & exit) -if /i "%~2" == "mime" (copy "%~1\clib\mime\mime.dll" "%~1\clib\" & exit) diff --git a/luaclib/Makefile b/luaclib/Makefile new file mode 100644 index 0000000..18fa944 --- /dev/null +++ b/luaclib/Makefile @@ -0,0 +1,39 @@ +.PHONY: all clean install + +ifdef PLATFORM +override PLATFORM := $(shell echo $(PLATFORM) | tr "[A-Z]" "[a-z]") +else +PLATFORM = $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"') +endif + +ROOT_PATH = $(shell pwd) + +define MAKE_PLATFORM + ##not essential builds + cd common && make PLATFORM=$(1) RELEASE=1; cd $(ROOT_PATH) + cd mime && make PLATFORM=$(1) RELEASE=1; cd $(ROOT_PATH) + cd cjson && make PLATFORM=$(1) RELEASE=1; cd $(ROOT_PATH) + cd lfs && make PLATFORM=$(1) RELEASE=1; cd $(ROOT_PATH) + cd lpeg && make PLATFORM=$(1) RELEASE=1; cd $(ROOT_PATH) + cd pb && make PLATFORM=$(1) RELEASE=1; cd $(ROOT_PATH) +endef + +all: + $(call MAKE_PLATFORM,$(PLATFORM)) + +clean: + ##not essential builds + cd common && make clean; cd $(ROOT_PATH) + cd mime && make clean; cd $(ROOT_PATH) + cd cjson && make clean; cd $(ROOT_PATH) + cd lfs && make clean; cd $(ROOT_PATH) + cd lpeg && make clean; cd $(ROOT_PATH) + cd pb && make clean; cd $(ROOT_PATH) + +install: + ##not essential install + cp mime/mime.so mime.so + cp cjson/cjson.so cjson.so + cp lfs/lfs.so lfs.so + cp lpeg/lpeg.so lpeg.so + cp pb/protobuf.so protobuf.so diff --git a/luaclib/cjson/CMakeLists.txt b/luaclib/cjson/CMakeLists.txt new file mode 100644 index 0000000..c17239b --- /dev/null +++ b/luaclib/cjson/CMakeLists.txt @@ -0,0 +1,76 @@ +# If Lua is installed in a non-standard location, please set the LUA_DIR +# environment variable to point to prefix for the install. Eg: +# Unix: export LUA_DIR=/home/user/pkg +# Windows: set LUA_DIR=c:\lua51 + +project(lua-cjson C) +cmake_minimum_required(VERSION 2.6) + +option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance") +option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + FORCE) +endif() + +find_package(Lua51 REQUIRED) +include_directories(${LUA_INCLUDE_DIR}) + +if(NOT USE_INTERNAL_FPCONV) + # Use libc number conversion routines (strtod(), sprintf()) + set(FPCONV_SOURCES fpconv.c) +else() + # Use internal number conversion routines + add_definitions(-DUSE_INTERNAL_FPCONV) + set(FPCONV_SOURCES g_fmt.c dtoa.c) + + include(TestBigEndian) + TEST_BIG_ENDIAN(IEEE_BIG_ENDIAN) + if(IEEE_BIG_ENDIAN) + add_definitions(-DIEEE_BIG_ENDIAN) + endif() + + if(MULTIPLE_THREADS) + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + find_package(Threads REQUIRED) + if(NOT CMAKE_USE_PTHREADS_INIT) + message(FATAL_ERROR + "Pthreads not found - required by MULTIPLE_THREADS option") + endif() + add_definitions(-DMULTIPLE_THREADS) + endif() +endif() + +# Handle platforms missing isinf() macro (Eg, some Solaris systems). +include(CheckSymbolExists) +CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) +if(NOT HAVE_ISINF) + add_definitions(-DUSE_INTERNAL_ISINF) +endif() + +set(_MODULE_LINK "${CMAKE_THREAD_LIBS_INIT}") +get_filename_component(_lua_lib_dir ${LUA_LIBRARY} PATH) + +if(APPLE) + set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS + "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") +endif() + +if(WIN32) + # Win32 modules need to be linked to the Lua library. + set(_MODULE_LINK ${LUA_LIBRARY} ${_MODULE_LINK}) + set(_lua_module_dir "${_lua_lib_dir}") + # Windows sprintf()/strtod() handle NaN/inf differently. Not supported. + add_definitions(-DDISABLE_INVALID_NUMBERS) +else() + set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") +endif() + +add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES}) +set_target_properties(cjson PROPERTIES PREFIX "") +target_link_libraries(cjson ${_MODULE_LINK}) +install(TARGETS cjson DESTINATION "${_lua_module_dir}") + +# vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/LICENSE b/luaclib/cjson/LICENSE new file mode 100644 index 0000000..747a8bf --- /dev/null +++ b/luaclib/cjson/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2010-2012 Mark Pulford + +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/cjson/Makefile b/luaclib/cjson/Makefile new file mode 100644 index 0000000..57feb07 --- /dev/null +++ b/luaclib/cjson/Makefile @@ -0,0 +1,90 @@ +##### Available defines for CJSON_CFLAGS ##### +## +## USE_INTERNAL_ISINF: Workaround for Solaris platforms missing isinf(). +## DISABLE_INVALID_NUMBERS: Permanently disable invalid JSON numbers: +## NaN, Infinity, hex. +## +## Optional built-in number conversion uses the following defines: +## USE_INTERNAL_FPCONV: Use builtin strtod/dtoa for numeric conversions. +## IEEE_BIG_ENDIAN: Required on big endian architectures. +## MULTIPLE_THREADS: Must be set when Lua CJSON may be used in a +## multi-threaded application. Requries _pthreads_. + +.PHONY: all clean +CC = gcc +RM = rm -rf +##PLATFORM ?= linux + +TARGET = cjson.so +all: $(TARGET) + +## source file path +SRC_PATH := . + +## used headers file path +INCLUDE_PATH := ../../deps/lua + +## used include librarys file path +LIBRARY_PATH := ../../deps/lua + +## need libs, add at here +LIBS := nlua + +## define CFLAGS +CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -fpic -D LUA_COMPAT_5_2 -D LUA_COMPAT_5_1 +ifeq (RELEASE,$(RELEASE)) +CFLAGS += -D RELEASE -O3 +endif + +ifeq (sunos,$(PLATFORM)) +CFLAGS += -D USE_INTERNAL_ISINF +endif + +##### Number conversion configuration ##### + +## Use Libc support for number conversion (default) +FPCONV_OBJS = fpconv.o + +## Use built in number conversion +#FPCONV_OBJS = g_fmt.o dtoa.o +#CFLAGS += -DUSE_INTERNAL_FPCONV + +## Compile built in number conversion for big endian architectures +#CFLAGS += -DIEEE_BIG_ENDIAN + + +## define LDFLAGS +LDFLAGS += -shared + +ifeq (darwin,$(PLATFORM)) +LDFLAGS += -undefined dynamic_lookup \ + -framework Foundation \ + -framework CoreServices \ + -framework ApplicationServices +endif + +ifeq (linux,$(PLATFORM)) +LIBS += dl +endif + +## get all source files +SRCS += $(wildcard $(SRC_PATH)/*.c) + +## all .o based on all .c +## OBJS := $(SRCS:.c=.o) +OBJS = lua_cjson.o strbuf.o $(FPCONV_OBJS) + +## get all include path +CFLAGS += $(foreach dir, $(INCLUDE_PATH), -I$(dir)) +.c.o: + $(CC) -c $(CFLAGS) -o $@ $< + +## get all library path +LDFLAGS += $(foreach lib, $(LIBRARY_PATH), -L$(lib)) +## get all librarys +LDFLAGS += $(foreach lib, $(LIBS), -l$(lib)) +$(TARGET): $(OBJS) + $(CC) -o $@ $(OBJS) $(LDFLAGS) + +clean: + $(RM) *.o $(TARGET) diff --git a/luaclib/cjson/NEWS b/luaclib/cjson/NEWS new file mode 100644 index 0000000..8927d6e --- /dev/null +++ b/luaclib/cjson/NEWS @@ -0,0 +1,44 @@ +Version 2.1.0 (Mar 1 2012) +* Added cjson.safe module interface which returns nil after an error +* Improved Makefile compatibility with Solaris make + +Version 2.0.0 (Jan 22 2012) +* Improved platform compatibility for strtod/sprintf locale workaround +* Added option to build with David Gay's dtoa.c for improved performance +* Added support for Lua 5.2 +* Added option to encode infinity/NaN as JSON null +* Fixed encode bug with a raised default limit and deeply nested tables +* Updated Makefile for compatibility with non-GNU make implementations +* Added CMake build support +* Added HTML manual +* Increased default nesting limit to 1000 +* Added support for re-entrant use of encode and decode +* Added support for installing lua2json and json2lua utilities +* Added encode_invalid_numbers() and decode_invalid_numbers() +* Added decode_max_depth() +* Removed registration of global cjson module table +* Removed refuse_invalid_numbers() + +Version 1.0.4 (Nov 30 2011) +* Fixed numeric conversion under locales with a comma decimal separator + +Version 1.0.3 (Sep 15 2011) +* Fixed detection of objects with numeric string keys +* Provided work around for missing isinf() on Solaris + +Version 1.0.2 (May 30 2011) +* Portability improvements for Windows + - No longer links with -lm + - Use "socket" instead of "posix" for sub-second timing +* Removed UTF-8 test dependency on Perl Text::Iconv +* Added simple CLI commands for testing Lua <-> JSON conversions +* Added cjson.encode_number_precision() + +Version 1.0.1 (May 10 2011) +* Added build support for OSX +* Removed unnecessary whitespace from JSON output +* Added cjson.encode_keep_buffer() +* Fixed memory leak on Lua stack overflow exception + +Version 1.0 (May 9 2011) +* Initial release diff --git a/luaclib/cjson/THANKS b/luaclib/cjson/THANKS new file mode 100644 index 0000000..4aade13 --- /dev/null +++ b/luaclib/cjson/THANKS @@ -0,0 +1,9 @@ +The following people have helped with bug reports, testing and/or +suggestions: + +- Louis-Philippe Perron (@loopole) +- Ondřej Jirman +- Steve Donovan +- Zhang "agentzh" Yichun + +Thanks! diff --git a/luaclib/cjson/cjson.def b/luaclib/cjson/cjson.def new file mode 100644 index 0000000..f070692 --- /dev/null +++ b/luaclib/cjson/cjson.def @@ -0,0 +1,3 @@ +LIBRARY + EXPORTS + luaopen_cjson \ No newline at end of file diff --git a/luaclib/cjson/cjson.vcxproj b/luaclib/cjson/cjson.vcxproj new file mode 100644 index 0000000..6542e3b --- /dev/null +++ b/luaclib/cjson/cjson.vcxproj @@ -0,0 +1,89 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {933BAD43-4B46-4BC1-A943-7825C5946B23} + cjson + + + + DynamicLibrary + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(ProjectDir) + + + + Level3 + Disabled + false + ..\..\deps\lua; + WIN32;_DEBUG;_WINDOWS;_USRDLL;LUA_CORE;LUA_BUILD_AS_DLL;LUA_COMPAT_5_2;LUA_COMPAT_5_1;%(PreprocessorDefinitions) + /D"inline"=__inline /D"snprintf"=_snprintf /D"strncasecmp"=strnicmp %(AdditionalOptions) + + + true + ..\..\deps\lua; + nlua.lib + cjson.def + + + copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir) + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/luaclib/cjson/cjson.vcxproj.filters b/luaclib/cjson/cjson.vcxproj.filters new file mode 100644 index 0000000..aa1e759 --- /dev/null +++ b/luaclib/cjson/cjson.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {61e8bb3e-7ce4-4806-95b8-6f7000191bff} + + + + + src + + + src + + + src + + + + + src + + + src + + + \ No newline at end of file diff --git a/clib/mime/mime.vcxproj.user b/luaclib/cjson/cjson.vcxproj.user similarity index 100% rename from clib/mime/mime.vcxproj.user rename to luaclib/cjson/cjson.vcxproj.user diff --git a/luaclib/cjson/dtoa.c b/luaclib/cjson/dtoa.c new file mode 100644 index 0000000..56398ba --- /dev/null +++ b/luaclib/cjson/dtoa.c @@ -0,0 +1,4358 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. This will cause dtoa modes 4 and 5 to be + * treated the same as modes 2 and 3 for some inputs. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS + * is also #defined, fegetround() will be queried for the rounding mode. + * Note that both FLT_ROUNDS and fegetround() are specified by the C99 + * standard (and are specified to be consistent, with fesetround() + * affecting the value of FLT_ROUNDS), but that some (Linux) systems + * do not work correctly in this regard, so using fegetround() is more + * portable than using FLT_ROUNDS directly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic + * that rounds toward +Infinity. + * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased + * rounding when the underlying floating-point arithmetic uses + * unbiased rounding. This prevent using ordinary floating-point + * arithmetic when the result could be computed with one rounding error. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a dtoa call after a dtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). On some systems + * (e.g., some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + * #define NO_HEX_FP to omit recognition of hexadecimal floating-point + * values by strtod. + * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now) + * to disable logic for "fast" testing of very long input strings + * to strtod. This testing proceeds by initially truncating the + * input string, then if necessary comparing the whole string with + * a decimal expansion to decide close cases. This logic is only + * used for input more than STRTOD_DIGLIM digits long (default 40). + */ + +#include "dtoa_config.h" + +#ifndef Long +#define Long long +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef Honor_FLT_ROUNDS +#ifndef Trust_FLT_ROUNDS +#include +#endif +#endif + +#ifdef MALLOC +#ifdef KR_headers +extern char *MALLOC(); +#else +extern void *MALLOC(size_t); +#endif +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#else +#undef INFNAN_CHECK +#define NO_STRTOD_BIGCOMP +#endif + +#include "errno.h" + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +typedef union { double d; ULong L[2]; } U; + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +#ifndef STRTOD_DIGLIM +#define STRTOD_DIGLIM 40 +#endif + +#ifdef DIGLIM_DEBUG +extern int strtod_diglim; +#else +#define strtod_diglim STRTOD_DIGLIM +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Nbits 53 +#define Bias 1023 +#define Emax 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Nbits 56 +#define Bias 65 +#define Emax 248 +#define Emin (-260) +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Nbits 56 +#define Bias 129 +#define Emax 126 +#define Emin (-129) +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#else +#ifdef ROUND_BIASED_without_Round_Up +#undef ROUND_BIASED +#define ROUND_BIASED +#endif +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +typedef struct BCinfo BCinfo; + struct +BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; }; + +#ifdef KR_headers +#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) +#else +#define FFFFFFFF 0xffffffffUL +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 7 + +#ifdef __cplusplus +extern "C" double fpconv_strtod(const char *s00, char **se); +extern "C" char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +#endif + + struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; + }; + + typedef struct Bigint Bigint; + + static Bigint *freelist[Kmax+1]; + + static Bigint * +Balloc +#ifdef KR_headers + (k) int k; +#else + (int k) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k])) + freelist[k] = rv->next; + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; + } + + static void +Bfree +#ifdef KR_headers + (v) Bigint *v; +#else + (Bigint *v) +#endif +{ + if (v) { + if (v->k > Kmax) +#ifdef FREE + FREE((void*)v); +#else + free((void*)v); +#endif + else { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } + } + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + + static Bigint * +multadd +#ifdef KR_headers + (b, m, a) Bigint *b; int m, a; +#else + (Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; + } + + static Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9; +#else + (const char *s, int nd0, int nd, ULong y9, int dplen) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s += dplen; + } + else + s += dplen + 9; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; + } + + static int +hi0bits +#ifdef KR_headers + (x) ULong x; +#else + (ULong x) +#endif +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + static int +lo0bits +#ifdef KR_headers + (y) ULong *y; +#else + (ULong *y) +#endif +{ + int k; + ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; + } + + static Bigint * +i2b +#ifdef KR_headers + (i) int i; +#else + (int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & FFFFFFFF; + } + while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + static Bigint *p5s; + + static Bigint * +pow5mult +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3)) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; + } + + static Bigint * +lshift +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z)) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; + } + + static int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + static Bigint * +diff +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + static double +ulp +#ifdef KR_headers + (x) U *x; +#else + (U *x) +#endif +{ + Long L; + U u; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(&u) = L; + word1(&u) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(&u) = 0x80000 >> L; + word1(&u) = 0; + } + else { + word0(&u) = 0; + L -= Exp_shift; + word1(&u) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(&u); + } + + static double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(&d) +#define d1 word1(&d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(&d) = d0 >> 16 | d0 << 16; + word1(&d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(&d); + } + + static Bigint * +d2b +#ifdef KR_headers + (d, e, bits) U *d; int *e, *bits; +#else + (U *d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + static double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + U da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(&da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&da) *= 1 << k; + } + else { + k = -k; + word0(&db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&db) *= 1 << k; + } +#else + if (k > 0) + word0(&da) += k*Exp_msk1; + else { + k = -k; + word0(&db) += k*Exp_msk1; + } +#endif + return dval(&da) / dval(&db); + } + + static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-256 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#undef Need_Hexdig +#ifdef INFNAN_CHECK +#ifndef No_Hex_NaN +#define Need_Hexdig +#endif +#endif + +#ifndef Need_Hexdig +#ifndef NO_HEX_FP +#define Need_Hexdig +#endif +#endif + +#ifdef Need_Hexdig /*{*/ +static unsigned char hexdig[256]; + + static void +#ifdef KR_headers +htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc; +#else +htinit(unsigned char *h, unsigned char *s, int inc) +#endif +{ + int i, j; + for(i = 0; (j = s[i]) !=0; i++) + h[j] = i + inc; + } + + static void +#ifdef KR_headers +hexdig_init() +#else +hexdig_init(void) +#endif +{ +#define USC (unsigned char *) + htinit(hexdig, USC "0123456789", 0x10); + htinit(hexdig, USC "abcdef", 0x10 + 10); + htinit(hexdig, USC "ABCDEF", 0x10 + 10); + } +#endif /* } Need_Hexdig */ + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + + static int +match +#ifdef KR_headers + (sp, t) char **sp, *t; +#else + (const char **sp, const char *t) +#endif +{ + int c, d; + CONST char *s = *sp; + + while((d = *t++)) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } + +#ifndef No_Hex_NaN + static void +hexnan +#ifdef KR_headers + (rvp, sp) U *rvp; CONST char **sp; +#else + (U *rvp, const char **sp) +#endif +{ + ULong c, x[2]; + CONST char *s; + int c1, havedig, udx0, xshift; + + if (!hexdig['0']) + hexdig_init(); + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') + ++s; + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) + s += 2; + while((c = *(CONST unsigned char*)++s)) { + if ((c1 = hexdig[c])) + c = c1 & 0xf; + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } +#ifdef GDTOA_NON_PEDANTIC_NANCHECK + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else + return; /* invalid form: don't change *sp */ +#else + else { + do { + if (/*(*/ c == ')') { + *sp = s + 1; + break; + } + } while((c = *++s)); + break; + } +#endif + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) + x[0] = (x[0] << 4) | (x[1] >> 28); + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(rvp) = Exp_mask | x[0]; + word1(rvp) = x[1]; + } + } +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + +#ifdef Pack_32 +#define ULbits 32 +#define kshift 5 +#define kmask 31 +#else +#define ULbits 16 +#define kshift 4 +#define kmask 15 +#endif + +#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/ + static Bigint * +#ifdef KR_headers +increment(b) Bigint *b; +#else +increment(Bigint *b) +#endif +{ + ULong *x, *xe; + Bigint *b1; + + x = b->x; + xe = x + b->wds; + do { + if (*x < (ULong)0xffffffffL) { + ++*x; + return b; + } + *x++ = 0; + } while(x < xe); + { + if (b->wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1,b); + Bfree(b); + b = b1; + } + b->x[b->wds++] = 1; + } + return b; + } + +#endif /*}*/ + +#ifndef NO_HEX_FP /*{*/ + + static void +#ifdef KR_headers +rshift(b, k) Bigint *b; int k; +#else +rshift(Bigint *b, int k) +#endif +{ + ULong *x, *x1, *xe, y; + int n; + + x = x1 = b->x; + n = k >> kshift; + if (n < b->wds) { + xe = x + b->wds; + x += n; + if (k &= kmask) { + n = 32 - k; + y = *x++ >> k; + while(x < xe) { + *x1++ = (y | (*x << n)) & 0xffffffff; + y = *x++ >> k; + } + if ((*x1 = y) !=0) + x1++; + } + else + while(x < xe) + *x1++ = *x++; + } + if ((b->wds = x1 - b->x) == 0) + b->x[0] = 0; + } + + static ULong +#ifdef KR_headers +any_on(b, k) Bigint *b; int k; +#else +any_on(Bigint *b, int k) +#endif +{ + int n, nwds; + ULong *x, *x0, x1, x2; + + x = b->x; + nwds = b->wds; + n = k >> kshift; + if (n > nwds) + n = nwds; + else if (n < nwds && (k &= kmask)) { + x1 = x2 = x[n]; + x1 >>= k; + x1 <<= k; + if (x1 != x2) + return 1; + } + x0 = x; + x += n; + while(x > x0) + if (*--x) + return 1; + return 0; + } + +enum { /* rounding values: same as FLT_ROUNDS */ + Round_zero = 0, + Round_near = 1, + Round_up = 2, + Round_down = 3 + }; + + void +#ifdef KR_headers +gethex(sp, rvp, rounding, sign) + CONST char **sp; U *rvp; int rounding, sign; +#else +gethex( CONST char **sp, U *rvp, int rounding, int sign) +#endif +{ + Bigint *b; + CONST unsigned char *decpt, *s0, *s, *s1; + Long e, e1; + ULong L, lostbits, *x; + int big, denorm, esign, havedig, k, n, nbits, up, zret; +#ifdef IBM + int j; +#endif + enum { +#ifdef IEEE_Arith /*{{*/ + emax = 0x7fe - Bias - P + 1, + emin = Emin - P + 1 +#else /*}{*/ + emin = Emin - P, +#ifdef VAX + emax = 0x7ff - Bias - P + 1 +#endif +#ifdef IBM + emax = 0x7f - Bias - P +#endif +#endif /*}}*/ + }; +#ifdef USE_LOCALE + int i; +#ifdef NO_LOCALE_CACHE + const unsigned char *decimalpoint = (unsigned char*) + localeconv()->decimal_point; +#else + const unsigned char *decimalpoint; + static unsigned char *decimalpoint_cache; + if (!(s0 = decimalpoint_cache)) { + s0 = (unsigned char*)localeconv()->decimal_point; + if ((decimalpoint_cache = (unsigned char*) + MALLOC(strlen((CONST char*)s0) + 1))) { + strcpy((char*)decimalpoint_cache, (CONST char*)s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#endif + + if (!hexdig['0']) + hexdig_init(); + havedig = 0; + s0 = *(CONST unsigned char **)sp + 2; + while(s0[havedig] == '0') + havedig++; + s0 += havedig; + s = s0; + decpt = 0; + zret = 0; + e = 0; + if (hexdig[*s]) + havedig++; + else { + zret = 1; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s != '.') + goto pcheck; + decpt = ++s; +#endif + if (!hexdig[*s]) + goto pcheck; + while(*s == '0') + s++; + if (hexdig[*s]) + zret = 0; + havedig = 1; + s0 = s; + } + while(hexdig[*s]) + s++; +#ifdef USE_LOCALE + if (*s == *decimalpoint && !decpt) { + for(i = 1; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s == '.' && !decpt) { + decpt = ++s; +#endif + while(hexdig[*s]) + s++; + }/*}*/ + if (decpt) + e = -(((Long)(s-decpt)) << 2); + pcheck: + s1 = s; + big = esign = 0; + switch(*s) { + case 'p': + case 'P': + switch(*++s) { + case '-': + esign = 1; + /* no break */ + case '+': + s++; + } + if ((n = hexdig[*s]) == 0 || n > 0x19) { + s = s1; + break; + } + e1 = n - 0x10; + while((n = hexdig[*++s]) !=0 && n <= 0x19) { + if (e1 & 0xf8000000) + big = 1; + e1 = 10*e1 + n - 0x10; + } + if (esign) + e1 = -e1; + e += e1; + } + *sp = (char*)s; + if (!havedig) + *sp = (char*)s0 - 1; + if (zret) + goto retz1; + if (big) { + if (esign) { +#ifdef IEEE_Arith + switch(rounding) { + case Round_up: + if (sign) + break; + goto ret_tiny; + case Round_down: + if (!sign) + break; + goto ret_tiny; + } +#endif + goto retz; +#ifdef IEEE_Arith + ret_tiny: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = 0; + word1(rvp) = 1; + return; +#endif /* IEEE_Arith */ + } + switch(rounding) { + case Round_near: + goto ovfl1; + case Round_up: + if (!sign) + goto ovfl1; + goto ret_big; + case Round_down: + if (sign) + goto ovfl1; + goto ret_big; + } + ret_big: + word0(rvp) = Big0; + word1(rvp) = Big1; + return; + } + n = s1 - s0 - 1; + for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) + k++; + b = Balloc(k); + x = b->x; + n = 0; + L = 0; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i+1]; ++i); +#endif + while(s1 > s0) { +#ifdef USE_LOCALE + if (*--s1 == decimalpoint[i]) { + s1 -= i; + continue; + } +#else + if (*--s1 == '.') + continue; +#endif + if (n == ULbits) { + *x++ = L; + L = 0; + n = 0; + } + L |= (hexdig[*s1] & 0x0f) << n; + n += 4; + } + *x++ = L; + b->wds = n = x - b->x; + n = ULbits*n - hi0bits(L); + nbits = Nbits; + lostbits = 0; + x = b->x; + if (n > nbits) { + n -= nbits; + if (any_on(b,n)) { + lostbits = 1; + k = n - 1; + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits = 2; + if (k > 0 && any_on(b,k)) + lostbits = 3; + } + } + rshift(b, n); + e += n; + } + else if (n < nbits) { + n = nbits - n; + b = lshift(b, n); + e -= n; + x = b->x; + } + if (e > Emax) { + ovfl: + Bfree(b); + ovfl1: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = Exp_mask; + word1(rvp) = 0; + return; + } + denorm = 0; + if (e < emin) { + denorm = 1; + n = emin - e; + if (n >= nbits) { +#ifdef IEEE_Arith /*{*/ + switch (rounding) { + case Round_near: + if (n == nbits && (n < 2 || any_on(b,n-1))) + goto ret_tiny; + break; + case Round_up: + if (!sign) + goto ret_tiny; + break; + case Round_down: + if (sign) + goto ret_tiny; + } +#endif /* } IEEE_Arith */ + Bfree(b); + retz: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + retz1: + rvp->d = 0.; + return; + } + k = n - 1; + if (lostbits) + lostbits = 1; + else if (k > 0) + lostbits = any_on(b,k); + if (x[k>>kshift] & 1 << (k & kmask)) + lostbits |= 2; + nbits -= n; + rshift(b,n); + e = emin; + } + if (lostbits) { + up = 0; + switch(rounding) { + case Round_zero: + break; + case Round_near: + if (lostbits & 2 + && (lostbits & 1) | (x[0] & 1)) + up = 1; + break; + case Round_up: + up = 1 - sign; + break; + case Round_down: + up = sign; + } + if (up) { + k = b->wds; + b = increment(b); + x = b->x; + if (denorm) { +#if 0 + if (nbits == Nbits - 1 + && x[nbits >> kshift] & 1 << (nbits & kmask)) + denorm = 0; /* not currently used */ +#endif + } + else if (b->wds > k + || ((n = nbits & kmask) !=0 + && hi0bits(x[k-1]) < 32-n)) { + rshift(b,1); + if (++e > Emax) + goto ovfl; + } + } + } +#ifdef IEEE_Arith + if (denorm) + word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0; + else + word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20); + word1(rvp) = b->x[0]; +#endif +#ifdef IBM + if ((j = e & 3)) { + k = b->x[0] & ((1 << j) - 1); + rshift(b,j); + if (k) { + switch(rounding) { + case Round_up: + if (!sign) + increment(b); + break; + case Round_down: + if (sign) + increment(b); + break; + case Round_near: + j = 1 << (j-1); + if (k & j && ((k & (j-1)) | lostbits)) + increment(b); + } + } + } + e >>= 2; + word0(rvp) = b->x[1] | ((e + 65 + 13) << 24); + word1(rvp) = b->x[0]; +#endif +#ifdef VAX + /* The next two lines ignore swap of low- and high-order 2 bytes. */ + /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */ + /* word1(rvp) = b->x[0]; */ + word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16); + word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16); +#endif + Bfree(b); + } +#endif /*!NO_HEX_FP}*/ + + static int +#ifdef KR_headers +dshift(b, p2) Bigint *b; int p2; +#else +dshift(Bigint *b, int p2) +#endif +{ + int rv = hi0bits(b->x[b->wds-1]) - 4; + if (p2 > 0) + rv -= p2; + return rv & kmask; + } + + static int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG +#ifdef NO_STRTOD_BIGCOMP + /*debug*/ if (q > 9) +#else + /* An oversized q is possible when quorem is called from bigcomp and */ + /* the input is near, e.g., twice the smallest denormalized number. */ + /*debug*/ if (q > 15) +#endif + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } + +#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/ + static double +sulp +#ifdef KR_headers + (x, bc) U *x; BCinfo *bc; +#else + (U *x, BCinfo *bc) +#endif +{ + U u; + double rv; + int i; + + rv = ulp(x); + if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) + return rv; /* Is there an example where i <= 0 ? */ + word0(&u) = Exp_1 + (i << Exp_shift); + word1(&u) = 0; + return rv * u.d; + } +#endif /*}*/ + +#ifndef NO_STRTOD_BIGCOMP + static void +bigcomp +#ifdef KR_headers + (rv, s0, bc) + U *rv; CONST char *s0; BCinfo *bc; +#else + (U *rv, const char *s0, BCinfo *bc) +#endif +{ + Bigint *b, *d; + int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase; + + dsign = bc->dsign; + nd = bc->nd; + nd0 = bc->nd0; + p5 = nd + bc->e0 - 1; + speccase = 0; +#ifndef Sudden_Underflow + if (rv->d == 0.) { /* special case: value near underflow-to-zero */ + /* threshold was rounded to zero */ + b = i2b(1); + p2 = Emin - P + 1; + bbits = 1; +#ifdef Avoid_Underflow + word0(rv) = (P+2) << Exp_shift; +#else + word1(rv) = 1; +#endif + i = 0; +#ifdef Honor_FLT_ROUNDS + if (bc->rounding == 1) +#endif + { + speccase = 1; + --p2; + dsign = 0; + goto have_i; + } + } + else +#endif + b = d2b(rv, &p2, &bbits); +#ifdef Avoid_Underflow + p2 -= bc->scale; +#endif + /* floor(log2(rv)) == bbits - 1 + p2 */ + /* Check for denormal case. */ + i = P - bbits; + if (i > (j = P - Emin - 1 + p2)) { +#ifdef Sudden_Underflow + Bfree(b); + b = i2b(1); + p2 = Emin; + i = P - 1; +#ifdef Avoid_Underflow + word0(rv) = (1 + bc->scale) << Exp_shift; +#else + word0(rv) = Exp_msk1; +#endif + word1(rv) = 0; +#else + i = j; +#endif + } +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (i > 0) + b = lshift(b, i); + if (dsign) + b = increment(b); + } + else +#endif + { + b = lshift(b, ++i); + b->x[0] |= 1; + } +#ifndef Sudden_Underflow + have_i: +#endif + p2 -= p5 + i; + d = i2b(1); + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + */ + if (p5 > 0) + d = pow5mult(d, p5); + else if (p5 < 0) + b = pow5mult(b, -p5); + if (p2 > 0) { + b2 = p2; + d2 = 0; + } + else { + b2 = 0; + d2 = -p2; + } + i = dshift(d, d2); + if ((b2 += i) > 0) + b = lshift(b, b2); + if ((d2 += i) > 0) + d = lshift(d, d2); + + /* Now b/d = exactly half-way between the two floating-point values */ + /* on either side of the input string. Compute first digit of b/d. */ + + if (!(dig = quorem(b,d))) { + b = multadd(b, 10, 0); /* very unlikely */ + dig = quorem(b,d); + } + + /* Compare b/d with s0 */ + + for(i = 0; i < nd0; ) { + if ((dd = s0[i++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + for(j = bc->dp1; i++ < nd;) { + if ((dd = s0[j++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + if (b->x[0] || b->wds > 1) + dd = -1; + ret: + Bfree(b); + Bfree(d); +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (dd < 0) { + if (bc->rounding == 0) { + if (!dsign) + goto retlow1; + } + else if (dsign) + goto rethi1; + } + else if (dd > 0) { + if (bc->rounding == 0) { + if (dsign) + goto rethi1; + goto ret1; + } + if (!dsign) + goto rethi1; + dval(rv) += 2.*sulp(rv,bc); + } + else { + bc->inexact = 0; + if (dsign) + goto rethi1; + } + } + else +#endif + if (speccase) { + if (dd <= 0) + rv->d = 0.; + } + else if (dd < 0) { + if (!dsign) /* does not happen for round-near */ +retlow1: + dval(rv) -= sulp(rv,bc); + } + else if (dd > 0) { + if (dsign) { + rethi1: + dval(rv) += sulp(rv,bc); + } + } + else { + /* Exact half-way case: apply round-even rule. */ + if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) { + i = 1 - j; + if (i <= 31) { + if (word1(rv) & (0x1 << i)) + goto odd; + } + else if (word0(rv) & (0x1 << (i-32))) + goto odd; + } + else if (word1(rv) & 1) { + odd: + if (dsign) + goto rethi1; + goto retlow1; + } + } + +#ifdef Honor_FLT_ROUNDS + ret1: +#endif + return; + } +#endif /* NO_STRTOD_BIGCOMP */ + + double +fpconv_strtod +#ifdef KR_headers + (s00, se) CONST char *s00; char **se; +#else + (const char *s00, char **se) +#endif +{ + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1; + int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1; + Long L; + U aadj2, adj, rv, rv0; + ULong y, z; + BCinfo bc; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef Avoid_Underflow + ULong Lsb, Lsb1; +#endif +#ifdef SET_INEXACT + int oldinexact; +#endif +#ifndef NO_STRTOD_BIGCOMP + int req_bigcomp = 0; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + bc.rounding = Flt_Rounds; +#else /*}{*/ + bc.rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: bc.rounding = 0; break; + case FE_UPWARD: bc.rounding = 2; break; + case FE_DOWNWARD: bc.rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ +#ifdef USE_LOCALE + CONST char *s2; +#endif + + sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0; + dval(&rv) = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { +#ifndef NO_HEX_FP /*{*/ + switch(s[1]) { + case 'x': + case 'X': +#ifdef Honor_FLT_ROUNDS + gethex(&s, &rv, bc.rounding, sign); +#else + gethex(&s, &rv, 1, sign); +#endif + goto ret; + } +#endif /*}*/ + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + bc.dp0 = bc.dp1 = s - s0; + for(s1 = s; s1 > s0 && *--s1 == '0'; ) + ++nz1; +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for(;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + bc.dp1 = s - s0; + bc.dplen = bc.dp1 - bc.dp0; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + bc.dp0 = s0 - s; + bc.dp1 = bc.dp0 + bc.dplen; + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = nz1 = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + if (!bc.dplen) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(&rv) = 0x7ff00000; + word1(&rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + hexnan(&rv, &s); +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ + ret0: + s = s00; + sign = 0; + } + goto ret; + } + bc.e0 = e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(&rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(&rv) = tens[k - 9] * dval(&rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) + goto ret; +#ifndef ROUND_BIASED_without_Round_Up + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + e -= i; + dval(&rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(&rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if ((word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(&rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(&rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + goto ret; + } +#endif +#endif /* ROUND_BIASED_without_Round_Up */ + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + bc.inexact = 1; + if (k <= DBL_DIG) + oldinexact = get_inexact(); +#endif +#ifdef Avoid_Underflow + bc.scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if (bc.rounding >= 2) { + if (sign) + bc.rounding = bc.rounding == 2 ? 0 : 2; + else + if (bc.rounding != 2) + bc.rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(&rv) = Big0; + word1(&rv) = Big1; + break; + default: + word0(&rv) = Exp_mask; + word1(&rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(&rv) = Exp_mask; + word1(&rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(&rv0) = 1e300; + dval(&rv0) *= dval(&rv0); +#endif +#else /*IEEE_Arith*/ + word0(&rv) = Big0; + word1(&rv) = Big1; +#endif /*IEEE_Arith*/ + range_err: + if (bd0) { + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + } +#ifndef NO_ERRNO + errno = ERANGE; +#endif + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(&rv) -= P*Exp_msk1; + dval(&rv) *= bigtens[j]; + if ((z = word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(&rv) = Big0; + word1(&rv) = Big1; + } + else + word0(&rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) + dval(&rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + bc.scale = 2*P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; clear j low bits */ + if (j >= 32) { + if (j > 54) + goto undfl; + word1(&rv) = 0; + if (j >= 53) + word0(&rv) = (P+2)*Exp_msk1; + else + word0(&rv) &= 0xffffffff << (j-32); + } + else + word1(&rv) &= 0xffffffff << j; + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(&rv0) = dval(&rv); + dval(&rv) *= tinytens[j]; + if (!dval(&rv)) { + dval(&rv) = 2.*dval(&rv0); + dval(&rv) *= tinytens[j]; +#endif + if (!dval(&rv)) { + undfl: + dval(&rv) = 0.; + goto range_err; + } +#ifndef Avoid_Underflow + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bc.nd = nd - nz1; +#ifndef NO_STRTOD_BIGCOMP + bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */ + /* to silence an erroneous warning about bc.nd0 */ + /* possibly not being initialized. */ + if (nd > strtod_diglim) { + /* ASSERT(strtod_diglim >= 18); 18 == one more than the */ + /* minimum number of decimal digits to distinguish double values */ + /* in IEEE arithmetic. */ + i = j = 18; + if (i > nd0) + j += bc.dplen; + for(;;) { + if (--j < bc.dp1 && j >= bc.dp0) + j = bc.dp0 - 1; + if (s0[j] != '0') + break; + --i; + } + e += nd - i; + nd = i; + if (nd0 > nd) + nd0 = nd; + if (nd < 9) { /* must recompute y */ + y = 0; + for(i = 0; i < nd0; ++i) + y = 10*y + s0[i] - '0'; + for(j = bc.dp1; i < nd; ++i) + y = 10*y + s0[j++] - '0'; + } + } +#endif + bd0 = s2b(s0, nd0, nd, y, bc.dplen); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(&rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + Lsb = LSB; + Lsb1 = 0; + j = bbe - bc.scale; + i = j + bbbits - 1; /* logb(rv) */ + j = P + 1 - bbbits; + if (i < Emin) { /* denormal */ + i = Emin - i; + j -= i; + if (i < 32) + Lsb <<= i; + else if (i < 52) + Lsb1 = Lsb << (i-32); + else + Lsb1 = Exp_mask; + } +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += bc.scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + bc.dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifndef NO_STRTOD_BIGCOMP /*{*/ + if (bc.nd > nd && i <= 0) { + if (bc.dsign) { + /* Must use bigcomp(). */ + req_bigcomp = 1; + break; + } +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) { + if (i < 0) { + req_bigcomp = 1; + break; + } + } + else +#endif + i = -1; /* Discarded digits make delta smaller. */ + } +#endif /*}*/ +#ifdef Honor_FLT_ROUNDS /*{*/ + if (bc.rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + if (bc.rounding) { + if (bc.dsign) { + adj.d = 1.; + goto apply_adj; + } + } + else if (!bc.dsign) { + adj.d = -1.; + if (!word1(&rv) + && !(word0(&rv) & Frac_mask)) { + y = word0(&rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!bc.scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) + adj.d = -0.5; + } + } + apply_adj: +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= + P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + dval(&rv) += adj.d*ulp(dval(&rv)); + word0(&rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + dval(&rv) += adj.d*ulp(&rv); + } + break; + } + adj.d = ratio(delta, bs); + if (adj.d < 1.) + adj.d = 1.; + if (adj.d <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj.d; + if (y != adj.d) { + if (!((bc.rounding>>1) ^ bc.dsign)) + y++; + adj.d = y; + } + } +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + adj.d *= ulp(dval(&rv)); + if (bc.dsign) + dval(&rv) += adj.d; + else + dval(&rv) -= adj.d; + word0(&rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + adj.d *= ulp(&rv); + if (bc.dsign) { + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + dval(&rv) += adj.d; + } + else + dval(&rv) -= adj.d; + goto cont; + } +#endif /*}Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask +#ifdef IEEE_Arith /*{*/ +#ifdef Avoid_Underflow + || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(&rv) & Exp_mask) <= Exp_msk1 +#endif +#endif /*}*/ + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + bc.inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (bc.dsign) { + if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 + && word1(&rv) == ( +#ifdef Avoid_Underflow + (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + word0(&rv) = (word0(&rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(&rv) = 0; +#ifdef Avoid_Underflow + bc.dsign = 0; +#endif + break; + } + } + else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(&rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (bc.scale) { + L = word0(&rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(&rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(&rv) = L | Bndry_mask1; + word1(&rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else +#ifndef NO_STRTOD_BIGCOMP + if (bc.nd > nd) + goto cont; +#endif + break; +#endif + } +#ifndef ROUND_BIASED +#ifdef Avoid_Underflow + if (Lsb1) { + if (!(word0(&rv) & Lsb1)) + break; + } + else if (!(word1(&rv) & Lsb)) + break; +#else + if (!(word1(&rv) & LSB)) + break; +#endif +#endif + if (bc.dsign) +#ifdef Avoid_Underflow + dval(&rv) += sulp(&rv, &bc); +#else + dval(&rv) += ulp(&rv); +#endif +#ifndef ROUND_BIASED + else { +#ifdef Avoid_Underflow + dval(&rv) -= sulp(&rv, &bc); +#else + dval(&rv) -= ulp(&rv); +#endif +#ifndef Sudden_Underflow + if (!dval(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + } +#ifdef Avoid_Underflow + bc.dsign = 1 - bc.dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (bc.dsign) + aadj = aadj1 = 1.; + else if (word1(&rv) || word0(&rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(&rv) == Tiny1 && !word0(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = bc.dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(bc.rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) + aadj1 += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(&rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(&rv0) = dval(&rv); + word0(&rv) -= P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if ((word0(&rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(&rv0) == Big0 && word1(&rv0) == Big1) + goto ovfl; + word0(&rv) = Big0; + word1(&rv) = Big1; + goto cont; + } + else + word0(&rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (bc.scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) + z = 1; + aadj = z; + aadj1 = bc.dsign ? aadj : -aadj; + } + dval(&aadj2) = aadj1; + word0(&aadj2) += (2*P+1)*Exp_msk1 - y; + aadj1 = dval(&aadj2); + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if (rv.d == 0.) +#ifdef NO_STRTOD_BIGCOMP + goto undfl; +#else + { + if (bc.nd > nd) + bc.dsign = 1; + break; + } +#endif + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + dval(&rv0) = dval(&rv); + word0(&rv) += P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#ifdef IBM + if ((word0(&rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(&rv0) == Tiny0 + && word1(&rv0) == Tiny1) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + goto cont; + } + else + word0(&rv) -= P*Exp_msk1; + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!bc.dsign) + aadj1 = -aadj1; + } + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(&rv) & Exp_mask; +#ifndef SET_INEXACT + if (bc.nd == nd) { +#ifdef Avoid_Underflow + if (!bc.scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } + } +#endif + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); +#ifndef NO_STRTOD_BIGCOMP + if (req_bigcomp) { + bd0 = 0; + bc.e0 += nz1; + bigcomp(&rv, s0, &bc); + y = word0(&rv) & Exp_mask; + if (y == Exp_mask) + goto ovfl; + if (y == 0 && rv.d == 0.) + goto undfl; + } +#endif +#ifdef SET_INEXACT + if (bc.inexact) { + if (!oldinexact) { + word0(&rv0) = Exp_1 + (70 << Exp_shift); + word1(&rv0) = 0; + dval(&rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif +#ifdef Avoid_Underflow + if (bc.scale) { + word0(&rv0) = Exp_1 - 2*P*Exp_msk1; + word1(&rv0) = 0; + dval(&rv) *= dval(&rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ +#ifdef IEEE_Arith + if (!(word0(&rv) & Exp_mask)) +#else + if (word0(&rv) == 0 && word1(&rv) == 0) +#endif + errno = ERANGE; +#endif + } +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT + if (bc.inexact && !(word0(&rv) & Exp_mask)) { + /* set underflow bit */ + dval(&rv0) = 1e-300; + dval(&rv0) *= dval(&rv0); + } +#endif + ret: + if (se) + *se = (char *)s; + return sign ? -dval(&rv) : dval(&rv); + } + +#ifndef MULTIPLE_THREADS + static char *dtoa_result; +#endif + + static char * +#ifdef KR_headers +rv_alloc(i) int i; +#else +rv_alloc(int i) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; + j <<= 1) + k++; + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); + } + + static char * +#ifdef KR_headers +nrv_alloc(s, rve, n) char *s, **rve; int n; +#else +nrv_alloc(const char *s, char **rve, int n) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++)) t++; + if (rve) + *rve = t; + return rv; + } + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + + void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + char * +dtoa +#ifdef KR_headers + (dd, mode, ndigits, decpt, sign, rve) + double dd; int mode, ndigits, *decpt, *sign; char **rve; +#else + (double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U d2, eps, u; + double ds; + char *s, *s0; +#ifndef No_leftright +#ifdef IEEE_Arith + U eps1; +#endif +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + u.d = dd; + if (word0(&u) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(&u) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(&u) & Exp_mask) == Exp_mask) +#else + if (word0(&u) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(&u) && !(word0(&u) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(&u) += 0; /* normalize */ +#endif + if (!dval(&u)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (*sign) + Rounding = Rounding == 2 ? 0 : 2; + else + if (Rounding != 2) + Rounding = 0; + } +#endif + + b = d2b(&u, &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + dval(&d2) = dval(&u); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) + dval(&d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32) + : word1(&u) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&u) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && Rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&u); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&u) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(&u) /= ds; + } + else if ((j1 = -k)) { + dval(&u) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&u) *= bigtens[i]; + } + } + if (k_check && dval(&u) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&u) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&u) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&u) -= 5.; + if (dval(&u) > dval(&eps)) + goto one_digit; + if (dval(&u) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); +#ifdef IEEE_Arith + if (k0 < 0 && j1 >= 307) { + eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */ + word0(&eps1) -= Exp_msk1 * (Bias+P-1); + dval(&eps1) *= tens[j1 & 0xf]; + for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++) + if (j & 1) + dval(&eps1) *= bigtens[i]; + if (eps.d < eps1.d) + eps.d = eps1.d; + } +#endif + for(i = 0;;) { + L = dval(&u); + dval(&u) -= L; + *s++ = '0' + (int)L; + if (1. - dval(&u) < dval(&eps)) + goto bump_up; + if (dval(&u) < dval(&eps)) + goto ret1; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&u) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u)); + if (!(dval(&u) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&u) > 0.5 + dval(&eps)) + goto bump_up; + else if (dval(&u) < 0.5 - dval(&eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(&u) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&u) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u) / ds); + dval(&u) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&u) < 0) { + L--; + dval(&u) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(&u)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(&u) += dval(&u); +#ifdef ROUND_BIASED + if (dval(&u) >= ds) +#else + if (dval(&u) > ds || (dval(&u) == ds && L & 1)) +#endif + { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5)) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && Rounding == 1 +#endif + ) { + if (!word1(&u) && !(word0(&u) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(&u) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + i = dshift(S, s2); + b2 += i; + m2 += i; + s2 += i; + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(&u) & 1) +#ifdef Honor_FLT_ROUNDS + && Rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(&u) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); +#ifdef ROUND_BIASED + if (j1 >= 0 /*)*/ +#else + if ((j1 > 0 || (j1 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!Rounding) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch(Rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1); + j = cmp(b, S); +#ifdef ROUND_BIASED + if (j >= 0) +#else + if (j > 0 || (j == 0 && dig & 1)) +#endif + { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { +#ifdef Honor_FLT_ROUNDS + trimzeros: +#endif + while(*--s == '0'); + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(&u) = Exp_1 + (70 << Exp_shift); + word1(&u) = 0; + dval(&u) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } +#ifdef __cplusplus +} +#endif diff --git a/luaclib/cjson/dtoa_config.h b/luaclib/cjson/dtoa_config.h new file mode 100644 index 0000000..380e83b --- /dev/null +++ b/luaclib/cjson/dtoa_config.h @@ -0,0 +1,74 @@ +#ifndef _DTOA_CONFIG_H +#define _DTOA_CONFIG_H + +#include +#include +#include + +/* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale + * aware conversion routines. */ +#undef USE_LOCALE + +/* dtoa.c should not touch errno, Lua CJSON does not use it, and it + * may not be threadsafe */ +#define NO_ERRNO + +#define Long int32_t +#define ULong uint32_t +#define Llong int64_t +#define ULLong uint64_t + +#ifdef IEEE_BIG_ENDIAN +#define IEEE_MC68k +#else +#define IEEE_8087 +#endif + +#define MALLOC(n) xmalloc(n) + +static void *xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) { + fprintf(stderr, "Out of memory"); + abort(); + } + + return p; +} + +#ifdef MULTIPLE_THREADS + +/* Enable locking to support multi-threaded applications */ + +#include + +static pthread_mutex_t private_dtoa_lock[2] = { + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER +}; + +#define ACQUIRE_DTOA_LOCK(n) do { \ + int r = pthread_mutex_lock(&private_dtoa_lock[n]); \ + if (r) { \ + fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \ + abort(); \ + } \ +} while (0) + +#define FREE_DTOA_LOCK(n) do { \ + int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \ + if (r) { \ + fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\ + abort(); \ + } \ +} while (0) + +#endif /* MULTIPLE_THREADS */ + +#endif /* _DTOA_CONFIG_H */ + +/* vi:ai et sw=4 ts=4: + */ diff --git a/luaclib/cjson/fpconv.c b/luaclib/cjson/fpconv.c new file mode 100644 index 0000000..7990831 --- /dev/null +++ b/luaclib/cjson/fpconv.c @@ -0,0 +1,205 @@ +/* fpconv - Floating point conversion routines + * + * Copyright (c) 2011-2012 Mark Pulford + * + * 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. + */ + +/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries + * with locale support will break when the decimal separator is a comma. + * + * fpconv_* will around these issues with a translation buffer if required. + */ + +#include +#include +#include +#include + +#include "fpconv.h" + +/* Lua CJSON assumes the locale is the same for all threads within a + * process and doesn't change after initialisation. + * + * This avoids the need for per thread storage or expensive checks + * for call. */ +static char locale_decimal_point = '.'; + +/* In theory multibyte decimal_points are possible, but + * Lua CJSON only supports UTF-8 and known locales only have + * single byte decimal points ([.,]). + * + * localconv() may not be thread safe (=>crash), and nl_langinfo() is + * not supported on some platforms. Use sprintf() instead - if the + * locale does change, at least Lua CJSON won't crash. */ +static void fpconv_update_locale() +{ + char buf[8]; + + snprintf(buf, sizeof(buf), "%g", 0.5); + + /* Failing this test might imply the platform has a buggy dtoa + * implementation or wide characters */ + if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { + fprintf(stderr, "Error: wide characters found or printf() bug."); + abort(); + } + + locale_decimal_point = buf[1]; +} + +/* Check for a valid number character: [-+0-9a-yA-Y.] + * Eg: -0.6e+5, infinity, 0xF0.F0pF0 + * + * Used to find the probable end of a number. It doesn't matter if + * invalid characters are counted - strtod() will find the valid + * number if it exists. The risk is that slightly more memory might + * be allocated before a parse error occurs. */ +static inline int valid_number_character(char ch) +{ + char lower_ch; + + if ('0' <= ch && ch <= '9') + return 1; + if (ch == '-' || ch == '+' || ch == '.') + return 1; + + /* Hex digits, exponent (e), base (p), "infinity",.. */ + lower_ch = ch | 0x20; + if ('a' <= lower_ch && lower_ch <= 'y') + return 1; + + return 0; +} + +/* Calculate the size of the buffer required for a strtod locale + * conversion. */ +static int strtod_buffer_size(const char *s) +{ + const char *p = s; + + while (valid_number_character(*p)) + p++; + + return p - s; +} + +/* Similar to strtod(), but must be passed the current locale's decimal point + * character. Guaranteed to be called at the start of any valid number in a string */ +double fpconv_strtod(const char *nptr, char **endptr) +{ + char localbuf[FPCONV_G_FMT_BUFSIZE]; + char *buf, *endbuf, *dp; + int buflen; + double value; + + /* System strtod() is fine when decimal point is '.' */ + if (locale_decimal_point == '.') + return strtod(nptr, endptr); + + buflen = strtod_buffer_size(nptr); + if (!buflen) { + /* No valid characters found, standard strtod() return */ + *endptr = (char *)nptr; + return 0; + } + + /* Duplicate number into buffer */ + if (buflen >= FPCONV_G_FMT_BUFSIZE) { + /* Handle unusually large numbers */ + buf = malloc(buflen + 1); + if (!buf) { + fprintf(stderr, "Out of memory"); + abort(); + } + } else { + /* This is the common case.. */ + buf = localbuf; + } + memcpy(buf, nptr, buflen); + buf[buflen] = 0; + + /* Update decimal point character if found */ + dp = strchr(buf, '.'); + if (dp) + *dp = locale_decimal_point; + + value = strtod(buf, &endbuf); + *endptr = (char *)&nptr[endbuf - buf]; + if (buflen >= FPCONV_G_FMT_BUFSIZE) + free(buf); + + return value; +} + +/* "fmt" must point to a buffer of at least 6 characters */ +static void set_number_format(char *fmt, int precision) +{ + int d1, d2, i; + + assert(1 <= precision && precision <= 14); + + /* Create printf format (%.14g) from precision */ + d1 = precision / 10; + d2 = precision % 10; + fmt[0] = '%'; + fmt[1] = '.'; + i = 2; + if (d1) { + fmt[i++] = '0' + d1; + } + fmt[i++] = '0' + d2; + fmt[i++] = 'g'; + fmt[i] = 0; +} + +/* Assumes there is always at least 32 characters available in the target buffer */ +int fpconv_g_fmt(char *str, double num, int precision) +{ + char buf[FPCONV_G_FMT_BUFSIZE]; + char fmt[6]; + int len; + char *b; + + set_number_format(fmt, precision); + + /* Pass through when decimal point character is dot. */ + if (locale_decimal_point == '.') + return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num); + + /* snprintf() to a buffer then translate for other decimal point characters */ + len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); + + /* Copy into target location. Translate decimal point if required */ + b = buf; + do { + *str++ = (*b == locale_decimal_point ? '.' : *b); + } while(*b++); + + return len; +} + +void fpconv_init() +{ + fpconv_update_locale(); +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/luaclib/cjson/fpconv.h b/luaclib/cjson/fpconv.h new file mode 100644 index 0000000..0124908 --- /dev/null +++ b/luaclib/cjson/fpconv.h @@ -0,0 +1,22 @@ +/* Lua CJSON floating point conversion routines */ + +/* Buffer required to store the largest string representation of a double. + * + * Longest double printed with %.14g is 21 characters long: + * -1.7976931348623e+308 */ +# define FPCONV_G_FMT_BUFSIZE 32 + +#ifdef USE_INTERNAL_FPCONV +static inline void fpconv_init() +{ + /* Do nothing - not required */ +} +#else +extern inline void fpconv_init(); +#endif + +extern int fpconv_g_fmt(char*, double, int); +extern double fpconv_strtod(const char*, char**); + +/* vi:ai et sw=4 ts=4: + */ diff --git a/luaclib/cjson/g_fmt.c b/luaclib/cjson/g_fmt.c new file mode 100644 index 0000000..50d6a1d --- /dev/null +++ b/luaclib/cjson/g_fmt.c @@ -0,0 +1,111 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 1996 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* g_fmt(buf,x) stores the closest decimal approximation to x in buf; + * it suffices to declare buf + * char buf[32]; + */ + +#ifdef __cplusplus +extern "C" { +#endif + extern char *dtoa(double, int, int, int *, int *, char **); + extern int g_fmt(char *, double, int); + extern void freedtoa(char*); +#ifdef __cplusplus + } +#endif + +int +fpconv_g_fmt(char *b, double x, int precision) +{ + register int i, k; + register char *s; + int decpt, j, sign; + char *b0, *s0, *se; + + b0 = b; +#ifdef IGNORE_ZERO_SIGN + if (!x) { + *b++ = '0'; + *b = 0; + goto done; + } +#endif + s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se); + if (sign) + *b++ = '-'; + if (decpt == 9999) /* Infinity or Nan */ { + while((*b++ = *s++)); + /* "b" is used to calculate the return length. Decrement to exclude the + * Null terminator from the length */ + b--; + goto done0; + } + if (decpt <= -4 || decpt > precision) { + *b++ = *s++; + if (*s) { + *b++ = '.'; + while((*b = *s++)) + b++; + } + *b++ = 'e'; + /* sprintf(b, "%+.2d", decpt - 1); */ + if (--decpt < 0) { + *b++ = '-'; + decpt = -decpt; + } + else + *b++ = '+'; + for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); + for(;;) { + i = decpt / k; + *b++ = i + '0'; + if (--j <= 0) + break; + decpt -= i*k; + decpt *= 10; + } + *b = 0; + } + else if (decpt <= 0) { + *b++ = '0'; + *b++ = '.'; + for(; decpt < 0; decpt++) + *b++ = '0'; + while((*b++ = *s++)); + b--; + } + else { + while((*b = *s++)) { + b++; + if (--decpt == 0 && *s) + *b++ = '.'; + } + for(; decpt > 0; decpt--) + *b++ = '0'; + *b = 0; + } + done0: + freedtoa(s0); +#ifdef IGNORE_ZERO_SIGN + done: +#endif + return b - b0; + } diff --git a/luaclib/cjson/lua-cjson-2.1devel-1.rockspec b/luaclib/cjson/lua-cjson-2.1devel-1.rockspec new file mode 100644 index 0000000..154e333 --- /dev/null +++ b/luaclib/cjson/lua-cjson-2.1devel-1.rockspec @@ -0,0 +1,56 @@ +package = "lua-cjson" +version = "2.1devel-1" + +source = { + url = "http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1devel.zip", +} + +description = { + summary = "A fast JSON encoding/parsing module", + detailed = [[ + The Lua CJSON module provides JSON support for Lua. It features: + - Fast, standards compliant encoding/parsing routines + - Full support for JSON with UTF-8, including decoding surrogate pairs + - Optional run-time support for common exceptions to the JSON specification + (infinity, NaN,..) + - No dependencies on other libraries + ]], + homepage = "http://www.kyne.com.au/~mark/software/lua-cjson.php", + license = "MIT" +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "builtin", + modules = { + cjson = { + sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" }, + defines = { +-- LuaRocks does not support platform specific configuration for Solaris. +-- Uncomment the line below on Solaris platforms if required. +-- "USE_INTERNAL_ISINF" + } + } + }, + install = { + lua = { + ["cjson.util"] = "lua/cjson/util.lua" + }, + bin = { + json2lua = "lua/json2lua.lua", + lua2json = "lua/lua2json.lua" + } + }, + -- Override default build options (per platform) + platforms = { + win32 = { modules = { cjson = { defines = { + "DISABLE_INVALID_NUMBERS" + } } } } + }, + copy_directories = { "tests" } +} + +-- vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/lua-cjson.spec b/luaclib/cjson/lua-cjson.spec new file mode 100644 index 0000000..13fc56d --- /dev/null +++ b/luaclib/cjson/lua-cjson.spec @@ -0,0 +1,80 @@ +%define luaver 5.1 +%define lualibdir %{_libdir}/lua/%{luaver} +%define luadatadir %{_datadir}/lua/%{luaver} + +Name: lua-cjson +Version: 2.1devel +Release: 1%{?dist} +Summary: A fast JSON encoding/parsing module for Lua + +Group: Development/Libraries +License: MIT +URL: http://www.kyne.com.au/~mark/software/lua-cjson/ +Source0: http://www.kyne.com.au/~mark/software/lua-cjson/download/lua-cjson-%{version}.tar.gz +BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) + +BuildRequires: lua >= %{luaver}, lua-devel >= %{luaver} +Requires: lua >= %{luaver} + +%description +The Lua CJSON module provides JSON support for Lua. It features: +- Fast, standards compliant encoding/parsing routines +- Full support for JSON with UTF-8, including decoding surrogate pairs +- Optional run-time support for common exceptions to the JSON specification + (infinity, NaN,..) +- No dependencies on other libraries + + +%prep +%setup -q + + +%build +make %{?_smp_mflags} CFLAGS="%{optflags}" LUA_INCLUDE_DIR="%{_includedir}" + + +%install +rm -rf "$RPM_BUILD_ROOT" +make install DESTDIR="$RPM_BUILD_ROOT" LUA_CMODULE_DIR="%{lualibdir}" +make install-extra DESTDIR="$RPM_BUILD_ROOT" LUA_MODULE_DIR="%{luadatadir}" \ + LUA_BIN_DIR="%{_bindir}" + + +%clean +rm -rf "$RPM_BUILD_ROOT" + + +%preun +/bin/rm -f "%{luadatadir}/cjson/tests/utf8.dat" + + +%files +%defattr(-,root,root,-) +%doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS +%{lualibdir}/* +%{luadatadir}/* +%{_bindir}/* + + +%changelog +* Thu Mar 1 2012 Mark Pulford - 2.1.0-1 +- Update for 2.1.0 + +* Sun Jan 22 2012 Mark Pulford - 2.0.0-1 +- Update for 2.0.0 +- Install lua2json / json2lua utilities + +* Wed Nov 27 2011 Mark Pulford - 1.0.4-1 +- Update for 1.0.4 + +* Wed Sep 15 2011 Mark Pulford - 1.0.3-1 +- Update for 1.0.3 + +* Sun May 29 2011 Mark Pulford - 1.0.2-1 +- Update for 1.0.2 + +* Sun May 10 2011 Mark Pulford - 1.0.1-1 +- Update for 1.0.1 + +* Sun May 1 2011 Mark Pulford - 1.0-1 +- Initial package diff --git a/luaclib/cjson/lua/cjson/util.lua b/luaclib/cjson/lua/cjson/util.lua new file mode 100644 index 0000000..6916dad --- /dev/null +++ b/luaclib/cjson/lua/cjson/util.lua @@ -0,0 +1,271 @@ +local json = require "cjson" + +-- Various common routines used by the Lua CJSON package +-- +-- Mark Pulford + +-- Determine with a Lua table can be treated as an array. +-- Explicitly returns "not an array" for very sparse arrays. +-- Returns: +-- -1 Not an array +-- 0 Empty table +-- >0 Highest index in the array +local function is_array(table) + local max = 0 + local count = 0 + for k, v in pairs(table) do + if type(k) == "number" then + if k > max then max = k end + count = count + 1 + else + return -1 + end + end + if max > count * 2 then + return -1 + end + + return max +end + +local serialise_value + +local function serialise_table(value, indent, depth) + local spacing, spacing2, indent2 + if indent then + spacing = "\n" .. indent + spacing2 = spacing .. " " + indent2 = indent .. " " + else + spacing, spacing2, indent2 = " ", " ", false + end + depth = depth + 1 + if depth > 50 then + return "Cannot serialise any further: too many nested tables" + end + + local max = is_array(value) + + local comma = false + local fragment = { "{" .. spacing2 } + if max > 0 then + -- Serialise array + for i = 1, max do + if comma then + table.insert(fragment, "," .. spacing2) + end + table.insert(fragment, serialise_value(value[i], indent2, depth)) + comma = true + end + elseif max < 0 then + -- Serialise table + for k, v in pairs(value) do + if comma then + table.insert(fragment, "," .. spacing2) + end + table.insert(fragment, + ("[%s] = %s"):format(serialise_value(k, indent2, depth), + serialise_value(v, indent2, depth))) + comma = true + end + end + table.insert(fragment, spacing .. "}") + + return table.concat(fragment) +end + +function serialise_value(value, indent, depth) + if indent == nil then indent = "" end + if depth == nil then depth = 0 end + + if value == json.null then + return "json.null" + elseif type(value) == "string" then + return ("%q"):format(value) + elseif type(value) == "nil" or type(value) == "number" or + type(value) == "boolean" then + return tostring(value) + elseif type(value) == "table" then + return serialise_table(value, indent, depth) + else + return "\"<" .. type(value) .. ">\"" + end +end + +local function file_load(filename) + local file + if filename == nil then + file = io.stdin + else + local err + file, err = io.open(filename, "rb") + if file == nil then + error(("Unable to read '%s': %s"):format(filename, err)) + end + end + local data = file:read("*a") + + if filename ~= nil then + file:close() + end + + if data == nil then + error("Failed to read " .. filename) + end + + return data +end + +local function file_save(filename, data) + local file + if filename == nil then + file = io.stdout + else + local err + file, err = io.open(filename, "wb") + if file == nil then + error(("Unable to write '%s': %s"):format(filename, err)) + end + end + file:write(data) + if filename ~= nil then + file:close() + end +end + +local function compare_values(val1, val2) + local type1 = type(val1) + local type2 = type(val2) + if type1 ~= type2 then + return false + end + + -- Check for NaN + if type1 == "number" and val1 ~= val1 and val2 ~= val2 then + return true + end + + if type1 ~= "table" then + return val1 == val2 + end + + -- check_keys stores all the keys that must be checked in val2 + local check_keys = {} + for k, _ in pairs(val1) do + check_keys[k] = true + end + + for k, v in pairs(val2) do + if not check_keys[k] then + return false + end + + if not compare_values(val1[k], val2[k]) then + return false + end + + check_keys[k] = nil + end + for k, _ in pairs(check_keys) do + -- Not the same if any keys from val1 were not found in val2 + return false + end + return true +end + +local test_count_pass = 0 +local test_count_total = 0 + +local function run_test_summary() + return test_count_pass, test_count_total +end + +local function run_test(testname, func, input, should_work, output) + local function status_line(name, status, value) + local statusmap = { [true] = ":success", [false] = ":error" } + if status ~= nil then + name = name .. statusmap[status] + end + print(("[%s] %s"):format(name, serialise_value(value, false))) + end + + local result = { pcall(func, unpack(input)) } + local success = table.remove(result, 1) + + local correct = false + if success == should_work and compare_values(result, output) then + correct = true + test_count_pass = test_count_pass + 1 + end + test_count_total = test_count_total + 1 + + local teststatus = { [true] = "PASS", [false] = "FAIL" } + print(("==> Test [%d] %s: %s"):format(test_count_total, testname, + teststatus[correct])) + + status_line("Input", nil, input) + if not correct then + status_line("Expected", should_work, output) + end + status_line("Received", success, result) + print() + + return correct, result +end + +local function run_test_group(tests) + local function run_helper(name, func, input) + if type(name) == "string" and #name > 0 then + print("==> " .. name) + end + -- Not a protected call, these functions should never generate errors. + func(unpack(input or {})) + print() + end + + for _, v in ipairs(tests) do + -- Run the helper if "should_work" is missing + if v[4] == nil then + run_helper(unpack(v)) + else + run_test(unpack(v)) + end + end +end + +-- Run a Lua script in a separate environment +local function run_script(script, env) + local env = env or {} + local func + + -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists + if _G.setfenv then + func = loadstring(script) + if func then + setfenv(func, env) + end + else + func = load(script, nil, nil, env) + end + + if func == nil then + error("Invalid syntax.") + end + func() + + return env +end + +-- Export functions +return { + serialise_value = serialise_value, + file_load = file_load, + file_save = file_save, + compare_values = compare_values, + run_test_summary = run_test_summary, + run_test = run_test, + run_test_group = run_test_group, + run_script = run_script +} + +-- vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/lua/json2lua.lua b/luaclib/cjson/lua/json2lua.lua new file mode 100644 index 0000000..014416d --- /dev/null +++ b/luaclib/cjson/lua/json2lua.lua @@ -0,0 +1,14 @@ +#!/usr/bin/env lua + +-- usage: json2lua.lua [json_file] +-- +-- Eg: +-- echo '[ "testing" ]' | ./json2lua.lua +-- ./json2lua.lua test.json + +local json = require "cjson" +local util = require "cjson.util" + +local json_text = util.file_load(arg[1]) +local t = json.decode(json_text) +print(util.serialise_value(t)) diff --git a/luaclib/cjson/lua/lua2json.lua b/luaclib/cjson/lua/lua2json.lua new file mode 100644 index 0000000..aee8869 --- /dev/null +++ b/luaclib/cjson/lua/lua2json.lua @@ -0,0 +1,20 @@ +#!/usr/bin/env lua + +-- usage: lua2json.lua [lua_file] +-- +-- Eg: +-- echo '{ "testing" }' | ./lua2json.lua +-- ./lua2json.lua test.lua + +local json = require "cjson" +local util = require "cjson.util" + +local env = { + json = { null = json.null }, + null = json.null +} + +local t = util.run_script("data = " .. util.file_load(arg[1]), env) +print(json.encode(t.data)) + +-- vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/lua_cjson.c b/luaclib/cjson/lua_cjson.c new file mode 100644 index 0000000..97f2350 --- /dev/null +++ b/luaclib/cjson/lua_cjson.c @@ -0,0 +1,1445 @@ +/* Lua CJSON - JSON support for Lua + * + * Copyright (c) 2010-2012 Mark Pulford + * + * 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. + */ + +/* Caveats: + * - JSON "null" values are represented as lightuserdata since Lua + * tables cannot contain "nil". Compare with cjson.null. + * - Invalid UTF-8 characters are not detected and will be passed + * untouched. If required, UTF-8 error checking should be done + * outside this library. + * - Javascript comments are not part of the JSON spec, and are not + * currently supported. + * + * Note: Decoding is slower than encoding. Lua spends significant + * time (30%) managing tables when parsing JSON since it is + * difficult to know object/array sizes ahead of time. + */ + +#include +#include +#include +#include +#include +#include + +#include "strbuf.h" +#include "fpconv.h" + +#ifndef CJSON_MODNAME +#define CJSON_MODNAME "cjson" +#endif + +#ifndef CJSON_VERSION +#define CJSON_VERSION "2.1devel" +#endif + +/* Workaround for Solaris platforms missing isinf() */ +#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF)) +#define isinf(x) (!isnan(x) && isnan((x) - (x))) +#endif + +#define DEFAULT_SPARSE_CONVERT 0 +#define DEFAULT_SPARSE_RATIO 2 +#define DEFAULT_SPARSE_SAFE 10 +#define DEFAULT_ENCODE_MAX_DEPTH 1000 +#define DEFAULT_DECODE_MAX_DEPTH 1000 +#define DEFAULT_ENCODE_INVALID_NUMBERS 0 +#define DEFAULT_DECODE_INVALID_NUMBERS 1 +#define DEFAULT_ENCODE_KEEP_BUFFER 1 +#define DEFAULT_ENCODE_NUMBER_PRECISION 14 +#define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1 + +#ifdef DISABLE_INVALID_NUMBERS +#undef DEFAULT_DECODE_INVALID_NUMBERS +#define DEFAULT_DECODE_INVALID_NUMBERS 0 +#endif + +typedef enum { + T_OBJ_BEGIN, + T_OBJ_END, + T_ARR_BEGIN, + T_ARR_END, + T_STRING, + T_NUMBER, + T_BOOLEAN, + T_NULL, + T_COLON, + T_COMMA, + T_END, + T_WHITESPACE, + T_ERROR, + T_UNKNOWN +} json_token_type_t; + +static const char *json_token_type_name[] = { + "T_OBJ_BEGIN", + "T_OBJ_END", + "T_ARR_BEGIN", + "T_ARR_END", + "T_STRING", + "T_NUMBER", + "T_BOOLEAN", + "T_NULL", + "T_COLON", + "T_COMMA", + "T_END", + "T_WHITESPACE", + "T_ERROR", + "T_UNKNOWN", + NULL +}; + +typedef struct { + json_token_type_t ch2token[256]; + char escape2char[256]; /* Decoding */ + + /* encode_buf is only allocated and used when + * encode_keep_buffer is set */ + strbuf_t encode_buf; + + int encode_sparse_convert; + int encode_sparse_ratio; + int encode_sparse_safe; + int encode_max_depth; + int encode_invalid_numbers; /* 2 => Encode as "null" */ + int encode_number_precision; + int encode_keep_buffer; + int encode_empty_table_as_object; + + int decode_invalid_numbers; + int decode_max_depth; +} json_config_t; + +typedef struct { + const char *data; + const char *ptr; + strbuf_t *tmp; /* Temporary storage for strings */ + json_config_t *cfg; + int current_depth; +} json_parse_t; + +typedef struct { + json_token_type_t type; + int index; + union { + const char *string; + double number; + int boolean; + } value; + int string_len; +} json_token_t; + +static const char *char2escape[256] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", + "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", + "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", + "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f", + NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* ===== CONFIGURATION ===== */ + +static json_config_t *json_fetch_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = lua_touserdata(l, lua_upvalueindex(1)); + if (!cfg) + luaL_error(l, "BUG: Unable to fetch CJSON configuration"); + + return cfg; +} + +/* Ensure the correct number of arguments have been provided. + * Pad with nil to allow other functions to simply check arg[i] + * to find whether an argument was provided */ +static json_config_t *json_arg_init(lua_State *l, int args) +{ + luaL_argcheck(l, lua_gettop(l) <= args, args + 1, + "found too many arguments"); + + while (lua_gettop(l) < args) + lua_pushnil(l); + + return json_fetch_config(l); +} + +/* Process integer options for configuration functions */ +static int json_integer_option(lua_State *l, int optindex, int *setting, + int min, int max) +{ + char errmsg[64]; + int value; + + if (!lua_isnil(l, optindex)) { + value = luaL_checkinteger(l, optindex); + snprintf(errmsg, sizeof(errmsg), "expected integer between %d and %d", min, max); + luaL_argcheck(l, min <= value && value <= max, 1, errmsg); + *setting = value; + } + + lua_pushinteger(l, *setting); + + return 1; +} + +/* Process enumerated arguments for a configuration function */ +static int json_enum_option(lua_State *l, int optindex, int *setting, + const char **options, int bool_true) +{ + static const char *bool_options[] = { "off", "on", NULL }; + + if (!options) { + options = bool_options; + bool_true = 1; + } + + if (!lua_isnil(l, optindex)) { + if (bool_true && lua_isboolean(l, optindex)) + *setting = lua_toboolean(l, optindex) * bool_true; + else + *setting = luaL_checkoption(l, optindex, NULL, options); + } + + if (bool_true && (*setting == 0 || *setting == bool_true)) + lua_pushboolean(l, *setting); + else + lua_pushstring(l, options[*setting]); + + return 1; +} + +/* Configures handling of extremely sparse arrays: + * convert: Convert extremely sparse arrays into objects? Otherwise error. + * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio + * safe: Always use an array when the max index <= safe */ +static int json_cfg_encode_sparse_array(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 3); + + json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1); + json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX); + json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX); + + return 3; +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_encode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX); +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_decode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX); +} + +/* Configures number precision when converting doubles to text */ +static int json_cfg_encode_number_precision(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14); +} + +/* Configures how to treat empty table when encode lua table */ +static int json_cfg_encode_empty_table_as_object(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_enum_option(l, 1, &cfg->encode_empty_table_as_object, NULL, 1); +} + +/* Configures JSON encoding buffer persistence */ +static int json_cfg_encode_keep_buffer(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + int old_value; + + old_value = cfg->encode_keep_buffer; + + json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1); + + /* Init / free the buffer if the setting has changed */ + if (old_value ^ cfg->encode_keep_buffer) { + if (cfg->encode_keep_buffer) + strbuf_init(&cfg->encode_buf, 0); + else + strbuf_free(&cfg->encode_buf); + } + + return 1; +} + +#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV) +void json_verify_invalid_number_setting(lua_State *l, int *setting) +{ + if (*setting == 1) { + *setting = 0; + luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported."); + } +} +#else +#define json_verify_invalid_number_setting(l, s) do { } while(0) +#endif + +static int json_cfg_encode_invalid_numbers(lua_State *l) +{ + static const char *options[] = { "off", "on", "null", NULL }; + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_cfg_decode_invalid_numbers(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_destroy_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = lua_touserdata(l, 1); + if (cfg) + strbuf_free(&cfg->encode_buf); + cfg = NULL; + + return 0; +} + +static void json_create_config(lua_State *l) +{ + json_config_t *cfg; + int i; + + cfg = lua_newuserdata(l, sizeof(*cfg)); + + /* Create GC method to clean up strbuf */ + lua_newtable(l); + lua_pushcfunction(l, json_destroy_config); + lua_setfield(l, -2, "__gc"); + lua_setmetatable(l, -2); + + cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; + cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; + cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; + cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; + cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; + cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; + cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; + cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; + cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; + cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT; + +#if DEFAULT_ENCODE_KEEP_BUFFER > 0 + strbuf_init(&cfg->encode_buf, 0); +#endif + + /* Decoding init */ + + /* Tag all characters as an error */ + for (i = 0; i < 256; i++) + cfg->ch2token[i] = T_ERROR; + + /* Set tokens that require no further processing */ + cfg->ch2token['{'] = T_OBJ_BEGIN; + cfg->ch2token['}'] = T_OBJ_END; + cfg->ch2token['['] = T_ARR_BEGIN; + cfg->ch2token[']'] = T_ARR_END; + cfg->ch2token[','] = T_COMMA; + cfg->ch2token[':'] = T_COLON; + cfg->ch2token['\0'] = T_END; + cfg->ch2token[' '] = T_WHITESPACE; + cfg->ch2token['\t'] = T_WHITESPACE; + cfg->ch2token['\n'] = T_WHITESPACE; + cfg->ch2token['\r'] = T_WHITESPACE; + + /* Update characters that require further processing */ + cfg->ch2token['f'] = T_UNKNOWN; /* false? */ + cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */ + cfg->ch2token['I'] = T_UNKNOWN; + cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */ + cfg->ch2token['N'] = T_UNKNOWN; + cfg->ch2token['t'] = T_UNKNOWN; /* true? */ + cfg->ch2token['"'] = T_UNKNOWN; /* string? */ + cfg->ch2token['+'] = T_UNKNOWN; /* number? */ + cfg->ch2token['-'] = T_UNKNOWN; + for (i = 0; i < 10; i++) + cfg->ch2token['0' + i] = T_UNKNOWN; + + /* Lookup table for parsing escape characters */ + for (i = 0; i < 256; i++) + cfg->escape2char[i] = 0; /* String error */ + cfg->escape2char['"'] = '"'; + cfg->escape2char['\\'] = '\\'; + cfg->escape2char['/'] = '/'; + cfg->escape2char['b'] = '\b'; + cfg->escape2char['t'] = '\t'; + cfg->escape2char['n'] = '\n'; + cfg->escape2char['f'] = '\f'; + cfg->escape2char['r'] = '\r'; + cfg->escape2char['u'] = 'u'; /* Unicode parsing required */ +} + +/* ===== ENCODING ===== */ + +static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex, + const char *reason) +{ + if (!cfg->encode_keep_buffer) + strbuf_free(json); + luaL_error(l, "Cannot serialise %s: %s", + lua_typename(l, lua_type(l, lindex)), reason); +} + +/* json_append_string args: + * - lua_State + * - JSON strbuf + * - String (Lua stack index) + * + * Returns nothing. Doesn't remove string from Lua stack */ +static void json_append_string(lua_State *l, strbuf_t *json, int lindex) +{ + const char *escstr; + int i; + const char *str; + size_t len; + + str = lua_tolstring(l, lindex, &len); + + /* Worst case is len * 6 (all unicode escapes). + * This buffer is reused constantly for small strings + * If there are any excess pages, they won't be hit anyway. + * This gains ~5% speedup. */ + strbuf_ensure_empty_length(json, len * 6 + 2); + + strbuf_append_char_unsafe(json, '\"'); + for (i = 0; i < len; i++) { + escstr = char2escape[(unsigned char)str[i]]; + if (escstr) + strbuf_append_string(json, escstr); + else + strbuf_append_char_unsafe(json, str[i]); + } + strbuf_append_char_unsafe(json, '\"'); +} + +/* Find the size of the array on the top of the Lua stack + * -1 object (not a pure array) + * >=0 elements in array + */ +static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) +{ + double k; + int max; + int items; + + max = 0; + items = 0; + + lua_pushnil(l); + /* table, startkey */ + while (lua_next(l, -2) != 0) { + /* table, key, value */ + if (lua_type(l, -2) == LUA_TNUMBER && + (k = lua_tonumber(l, -2))) { + /* Integer >= 1 ? */ + if (floor(k) == k && k >= 1) { + if (k > max) + max = k; + items++; + lua_pop(l, 1); + continue; + } + } + + /* Must not be an array (non integer key) */ + lua_pop(l, 2); + return -1; + } + + /* Encode excessively sparse arrays as objects (if enabled) */ + if (cfg->encode_sparse_ratio > 0 && + max > items * cfg->encode_sparse_ratio && + max > cfg->encode_sparse_safe) { + if (!cfg->encode_sparse_convert) + json_encode_exception(l, cfg, json, -1, "excessively sparse array"); + + return -1; + } + + return max; +} + +static void json_check_encode_depth(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + /* Ensure there are enough slots free to traverse a table (key, + * value) and push a string for a potential error message. + * + * Unlike "decode", the key and value are still on the stack when + * lua_checkstack() is called. Hence an extra slot for luaL_error() + * below is required just in case the next check to lua_checkstack() + * fails. + * + * While this won't cause a crash due to the EXTRA_STACK reserve + * slots, it would still be an improper use of the API. */ + if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3)) + return; + + if (!cfg->encode_keep_buffer) + strbuf_free(json); + + luaL_error(l, "Cannot serialise, excessive nesting (%d)", + current_depth); +} + +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json); + +/* json_append_array args: + * - lua_State + * - JSON strbuf + * - Size of passwd Lua array (top of stack) */ +static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth, + strbuf_t *json, int array_length) +{ + int comma, i; + + strbuf_append_char(json, '['); + + comma = 0; + for (i = 1; i <= array_length; i++) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + lua_rawgeti(l, -1, i); + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + } + + strbuf_append_char(json, ']'); +} + +static void json_append_number(lua_State *l, json_config_t *cfg, + strbuf_t *json, int lindex) +{ + double num = lua_tonumber(l, lindex); + int len; + + if (cfg->encode_invalid_numbers == 0) { + /* Prevent encoding invalid numbers */ + if (isinf(num) || isnan(num)) + json_encode_exception(l, cfg, json, lindex, + "must not be NaN or Infinity"); + } else if (cfg->encode_invalid_numbers == 1) { + /* Encode NaN/Infinity separately to ensure Javascript compatible + * values are used. */ + if (isnan(num)) { + strbuf_append_mem(json, "NaN", 3); + return; + } + if (isinf(num)) { + if (num < 0) + strbuf_append_mem(json, "-Infinity", 9); + else + strbuf_append_mem(json, "Infinity", 8); + return; + } + } else { + /* Encode invalid numbers as "null" */ + if (isinf(num) || isnan(num)) { + strbuf_append_mem(json, "null", 4); + return; + } + } + + strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); + len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); + strbuf_extend_length(json, len); +} + +static void json_append_object(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int comma, keytype; + + /* Object */ + strbuf_append_char(json, '{'); + + lua_pushnil(l); + /* table, startkey */ + comma = 0; + while (lua_next(l, -2) != 0) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + /* table, key, value */ + keytype = lua_type(l, -2); + if (keytype == LUA_TNUMBER) { + strbuf_append_char(json, '"'); + json_append_number(l, cfg, json, -2); + strbuf_append_mem(json, "\":", 2); + } else if (keytype == LUA_TSTRING) { + json_append_string(l, json, -2); + strbuf_append_char(json, ':'); + } else { + json_encode_exception(l, cfg, json, -2, + "table key must be a number or string"); + /* never returns */ + } + + /* table, key, value */ + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + /* table, key */ + } + + strbuf_append_char(json, '}'); +} + +/* Serialise Lua data into JSON string. */ +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int len; + + switch (lua_type(l, -1)) { + case LUA_TSTRING: + json_append_string(l, json, -1); + break; + case LUA_TNUMBER: + json_append_number(l, cfg, json, -1); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(l, -1)) + strbuf_append_mem(json, "true", 4); + else + strbuf_append_mem(json, "false", 5); + break; + case LUA_TTABLE: + current_depth++; + json_check_encode_depth(l, cfg, current_depth, json); + len = lua_array_length(l, cfg, json); + if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) + json_append_array(l, cfg, current_depth, json, len); + else + json_append_object(l, cfg, current_depth, json); + break; + case LUA_TNIL: + strbuf_append_mem(json, "null", 4); + break; + case LUA_TLIGHTUSERDATA: + if (lua_touserdata(l, -1) == NULL) { + strbuf_append_mem(json, "null", 4); + break; + } + default: + /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, + * and LUA_TLIGHTUSERDATA) cannot be serialised */ + json_encode_exception(l, cfg, json, -1, "type not supported"); + /* never returns */ + } +} + +static int json_encode(lua_State *l) +{ + json_config_t *cfg = json_fetch_config(l); + strbuf_t local_encode_buf; + strbuf_t *encode_buf; + char *json; + int len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + if (!cfg->encode_keep_buffer) { + /* Use private buffer */ + encode_buf = &local_encode_buf; + strbuf_init(encode_buf, 0); + } else { + /* Reuse existing buffer */ + encode_buf = &cfg->encode_buf; + strbuf_reset(encode_buf); + } + + json_append_data(l, cfg, 0, encode_buf); + json = strbuf_string(encode_buf, &len); + + lua_pushlstring(l, json, len); + + if (!cfg->encode_keep_buffer) + strbuf_free(encode_buf); + + return 1; +} + +/* ===== DECODING ===== */ + +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token); + +static int hexdigit2int(char hex) +{ + if ('0' <= hex && hex <= '9') + return hex - '0'; + + /* Force lowercase */ + hex |= 0x20; + if ('a' <= hex && hex <= 'f') + return 10 + hex - 'a'; + + return -1; +} + +static int decode_hex4(const char *hex) +{ + int digit[4]; + int i; + + /* Convert ASCII hex digit to numeric digit + * Note: this returns an error for invalid hex digits, including + * NULL */ + for (i = 0; i < 4; i++) { + digit[i] = hexdigit2int(hex[i]); + if (digit[i] < 0) { + return -1; + } + } + + return (digit[0] << 12) + + (digit[1] << 8) + + (digit[2] << 4) + + digit[3]; +} + +/* Converts a Unicode codepoint to UTF-8. + * Returns UTF-8 string length, and up to 4 bytes in *utf8 */ +static int codepoint_to_utf8(char *utf8, int codepoint) +{ + /* 0xxxxxxx */ + if (codepoint <= 0x7F) { + utf8[0] = codepoint; + return 1; + } + + /* 110xxxxx 10xxxxxx */ + if (codepoint <= 0x7FF) { + utf8[0] = (codepoint >> 6) | 0xC0; + utf8[1] = (codepoint & 0x3F) | 0x80; + return 2; + } + + /* 1110xxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0xFFFF) { + utf8[0] = (codepoint >> 12) | 0xE0; + utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[2] = (codepoint & 0x3F) | 0x80; + return 3; + } + + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0x1FFFFF) { + utf8[0] = (codepoint >> 18) | 0xF0; + utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80; + utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[3] = (codepoint & 0x3F) | 0x80; + return 4; + } + + return 0; +} + + +/* Called when index pointing to beginning of UTF-16 code escape: \uXXXX + * \u is guaranteed to exist, but the remaining hex characters may be + * missing. + * Translate to UTF-8 and append to temporary token string. + * Must advance index to the next character to be processed. + * Returns: 0 success + * -1 error + */ +static int json_append_unicode_escape(json_parse_t *json) +{ + char utf8[4]; /* Surrogate pairs require 4 UTF-8 bytes */ + int codepoint; + int surrogate_low; + int len; + int escape_len = 6; + + /* Fetch UTF-16 code unit */ + codepoint = decode_hex4(json->ptr + 2); + if (codepoint < 0) + return -1; + + /* UTF-16 surrogate pairs take the following 2 byte form: + * 11011 x yyyyyyyyyy + * When x = 0: y is the high 10 bits of the codepoint + * x = 1: y is the low 10 bits of the codepoint + * + * Check for a surrogate pair (high or low) */ + if ((codepoint & 0xF800) == 0xD800) { + /* Error if the 1st surrogate is not high */ + if (codepoint & 0x400) + return -1; + + /* Ensure the next code is a unicode escape */ + if (*(json->ptr + escape_len) != '\\' || + *(json->ptr + escape_len + 1) != 'u') { + return -1; + } + + /* Fetch the next codepoint */ + surrogate_low = decode_hex4(json->ptr + 2 + escape_len); + if (surrogate_low < 0) + return -1; + + /* Error if the 2nd code is not a low surrogate */ + if ((surrogate_low & 0xFC00) != 0xDC00) + return -1; + + /* Calculate Unicode codepoint */ + codepoint = (codepoint & 0x3FF) << 10; + surrogate_low &= 0x3FF; + codepoint = (codepoint | surrogate_low) + 0x10000; + escape_len = 12; + } + + /* Convert codepoint to UTF-8 */ + len = codepoint_to_utf8(utf8, codepoint); + if (!len) + return -1; + + /* Append bytes and advance parse index */ + strbuf_append_mem_unsafe(json->tmp, utf8, len); + json->ptr += escape_len; + + return 0; +} + +static void json_set_token_error(json_token_t *token, json_parse_t *json, + const char *errtype) +{ + token->type = T_ERROR; + token->index = json->ptr - json->data; + token->value.string = errtype; +} + +static void json_next_string_token(json_parse_t *json, json_token_t *token) +{ + char *escape2char = json->cfg->escape2char; + char ch; + + /* Caller must ensure a string is next */ + assert(*json->ptr == '"'); + + /* Skip " */ + json->ptr++; + + /* json->tmp is the temporary strbuf used to accumulate the + * decoded string value. + * json->tmp is sized to handle JSON containing only a string value. + */ + strbuf_reset(json->tmp); + + while ((ch = *json->ptr) != '"') { + if (!ch) { + /* Premature end of the string */ + json_set_token_error(token, json, "unexpected end of string"); + return; + } + + /* Handle escapes */ + if (ch == '\\') { + /* Fetch escape character */ + ch = *(json->ptr + 1); + + /* Translate escape code and append to tmp string */ + ch = escape2char[(unsigned char)ch]; + if (ch == 'u') { + if (json_append_unicode_escape(json) == 0) + continue; + + json_set_token_error(token, json, + "invalid unicode escape code"); + return; + } + if (!ch) { + json_set_token_error(token, json, "invalid escape code"); + return; + } + + /* Skip '\' */ + json->ptr++; + } + /* Append normal character or translated single character + * Unicode escapes are handled above */ + strbuf_append_char_unsafe(json->tmp, ch); + json->ptr++; + } + json->ptr++; /* Eat final quote (") */ + + strbuf_ensure_null(json->tmp); + + token->type = T_STRING; + token->value.string = strbuf_string(json->tmp, &token->string_len); +} + +/* JSON numbers should take the following form: + * -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)? + * + * json_next_number_token() uses strtod() which allows other forms: + * - numbers starting with '+' + * - NaN, -NaN, infinity, -infinity + * - hexadecimal numbers + * - numbers with leading zeros + * + * json_is_invalid_number() detects "numbers" which may pass strtod()'s + * error checking, but should not be allowed with strict JSON. + * + * json_is_invalid_number() may pass numbers which cause strtod() + * to generate an error. + */ +static int json_is_invalid_number(json_parse_t *json) +{ + const char *p = json->ptr; + + /* Reject numbers starting with + */ + if (*p == '+') + return 1; + + /* Skip minus sign if it exists */ + if (*p == '-') + p++; + + /* Reject numbers starting with 0x, or leading zeros */ + if (*p == '0') { + int ch2 = *(p + 1); + + if ((ch2 | 0x20) == 'x' || /* Hex */ + ('0' <= ch2 && ch2 <= '9')) /* Leading zero */ + return 1; + + return 0; + } else if (*p <= '9') { + return 0; /* Ordinary number */ + } + + /* Reject inf/nan */ + if (!strncasecmp(p, "inf", 3)) + return 1; + if (!strncasecmp(p, "nan", 3)) + return 1; + + /* Pass all other numbers which may still be invalid, but + * strtod() will catch them. */ + return 0; +} + +static void json_next_number_token(json_parse_t *json, json_token_t *token) +{ + char *endptr; + + token->type = T_NUMBER; + token->value.number = fpconv_strtod(json->ptr, &endptr); + if (json->ptr == endptr) + json_set_token_error(token, json, "invalid number"); + else + json->ptr = endptr; /* Skip the processed number */ + + return; +} + +/* Fills in the token struct. + * T_STRING will return a pointer to the json_parse_t temporary string + * T_ERROR will leave the json->ptr pointer at the error. + */ +static void json_next_token(json_parse_t *json, json_token_t *token) +{ + const json_token_type_t *ch2token = json->cfg->ch2token; + int ch; + + /* Eat whitespace. */ + while (1) { + ch = (unsigned char)*(json->ptr); + token->type = ch2token[ch]; + if (token->type != T_WHITESPACE) + break; + json->ptr++; + } + + /* Store location of new token. Required when throwing errors + * for unexpected tokens (syntax errors). */ + token->index = json->ptr - json->data; + + /* Don't advance the pointer for an error or the end */ + if (token->type == T_ERROR) { + json_set_token_error(token, json, "invalid token"); + return; + } + + if (token->type == T_END) { + return; + } + + /* Found a known single character token, advance index and return */ + if (token->type != T_UNKNOWN) { + json->ptr++; + return; + } + + /* Process characters which triggered T_UNKNOWN + * + * Must use strncmp() to match the front of the JSON string. + * JSON identifier must be lowercase. + * When strict_numbers if disabled, either case is allowed for + * Infinity/NaN (since we are no longer following the spec..) */ + if (ch == '"') { + json_next_string_token(json, token); + return; + } else if (ch == '-' || ('0' <= ch && ch <= '9')) { + if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) { + json_set_token_error(token, json, "invalid number"); + return; + } + json_next_number_token(json, token); + return; + } else if (!strncmp(json->ptr, "true", 4)) { + token->type = T_BOOLEAN; + token->value.boolean = 1; + json->ptr += 4; + return; + } else if (!strncmp(json->ptr, "false", 5)) { + token->type = T_BOOLEAN; + token->value.boolean = 0; + json->ptr += 5; + return; + } else if (!strncmp(json->ptr, "null", 4)) { + token->type = T_NULL; + json->ptr += 4; + return; + } else if (json->cfg->decode_invalid_numbers && + json_is_invalid_number(json)) { + /* When decode_invalid_numbers is enabled, only attempt to process + * numbers we know are invalid JSON (Inf, NaN, hex) + * This is required to generate an appropriate token error, + * otherwise all bad tokens will register as "invalid number" + */ + json_next_number_token(json, token); + return; + } + + /* Token starts with t/f/n but isn't recognised above. */ + json_set_token_error(token, json, "invalid token"); +} + +/* This function does not return. + * DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED. + * The only supported exception is the temporary parser string + * json->tmp struct. + * json and token should exist on the stack somewhere. + * luaL_error() will long_jmp and release the stack */ +static void json_throw_parse_error(lua_State *l, json_parse_t *json, + const char *exp, json_token_t *token) +{ + const char *found; + + strbuf_free(json->tmp); + + if (token->type == T_ERROR) + found = token->value.string; + else + found = json_token_type_name[token->type]; + + /* Note: token->index is 0 based, display starting from 1 */ + luaL_error(l, "Expected %s but found %s at character %d", + exp, found, token->index + 1); +} + +static inline void json_decode_ascend(json_parse_t *json) +{ + json->current_depth--; +} + +static void json_decode_descend(lua_State *l, json_parse_t *json, int slots) +{ + json->current_depth++; + + if (json->current_depth <= json->cfg->decode_max_depth && + lua_checkstack(l, slots)) { + return; + } + + strbuf_free(json->tmp); + luaL_error(l, "Found too many nested data structures (%d) at character %d", + json->current_depth, json->ptr - json->data); +} + +static void json_parse_object_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + + /* 3 slots required: + * .., table, key, value */ + json_decode_descend(l, json, 3); + + lua_newtable(l); + + json_next_token(json, &token); + + /* Handle empty objects */ + if (token.type == T_OBJ_END) { + json_decode_ascend(json); + return; + } + + while (1) { + if (token.type != T_STRING) + json_throw_parse_error(l, json, "object key string", &token); + + /* Push key */ + lua_pushlstring(l, token.value.string, token.string_len); + + json_next_token(json, &token); + if (token.type != T_COLON) + json_throw_parse_error(l, json, "colon", &token); + + /* Fetch value */ + json_next_token(json, &token); + json_process_value(l, json, &token); + + /* Set key = value */ + lua_rawset(l, -3); + + json_next_token(json, &token); + + if (token.type == T_OBJ_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or object end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the array context */ +static void json_parse_array_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + int i; + + /* 2 slots required: + * .., table, value */ + json_decode_descend(l, json, 2); + + lua_newtable(l); + + json_next_token(json, &token); + + /* Handle empty arrays */ + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + for (i = 1; ; i++) { + json_process_value(l, json, &token); + lua_rawseti(l, -2, i); /* arr[i] = value */ + + json_next_token(json, &token); + + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or array end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the "value" context */ +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token) +{ + switch (token->type) { + case T_STRING: + lua_pushlstring(l, token->value.string, token->string_len); + break;; + case T_NUMBER: + lua_pushnumber(l, token->value.number); + break;; + case T_BOOLEAN: + lua_pushboolean(l, token->value.boolean); + break;; + case T_OBJ_BEGIN: + json_parse_object_context(l, json); + break;; + case T_ARR_BEGIN: + json_parse_array_context(l, json); + break;; + case T_NULL: + /* In Lua, setting "t[k] = nil" will delete k from the table. + * Hence a NULL pointer lightuserdata object is used instead */ + lua_pushlightuserdata(l, NULL); + break;; + default: + json_throw_parse_error(l, json, "value", token); + } +} + +static int json_decode(lua_State *l) +{ + json_parse_t json; + json_token_t token; + size_t json_len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + json.cfg = json_fetch_config(l); + json.data = luaL_checklstring(l, 1, &json_len); + json.current_depth = 0; + json.ptr = json.data; + + /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3) + * + * CJSON can support any simple data type, hence only the first + * character is guaranteed to be ASCII (at worst: '"'). This is + * still enough to detect whether the wrong encoding is in use. */ + if (json_len >= 2 && (!json.data[0] || !json.data[1])) + luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); + + /* Ensure the temporary buffer can hold the entire string. + * This means we no longer need to do length checks since the decoded + * string must be smaller than the entire json string */ + json.tmp = strbuf_new(json_len); + + json_next_token(&json, &token); + json_process_value(l, &json, &token); + + /* Ensure there is no more input left */ + json_next_token(&json, &token); + + if (token.type != T_END) + json_throw_parse_error(l, &json, "the end", &token); + + strbuf_free(json.tmp); + + return 1; +} + +/* ===== INITIALISATION ===== */ + +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +/* Compatibility for Lua 5.1. + * + * luaL_setfuncs() is used to create a module table where the functions have + * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */ +static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup) +{ + int i; + + luaL_checkstack(l, nup, "too many upvalues"); + for (; reg->name != NULL; reg++) { /* fill the table with given functions */ + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(l, -nup); + lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ + lua_setfield(l, -(nup + 2), reg->name); + } + lua_pop(l, nup); /* remove upvalues */ +} +#endif + +/* Call target function in protected mode with all supplied args. + * Assumes target function only returns a single non-nil value. + * Convert and return thrown errors as: nil, "error message" */ +static int json_protect_conversion(lua_State *l) +{ + int err; + + /* Deliberately throw an error for invalid arguments */ + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + /* pcall() the function stored as upvalue(1) */ + lua_pushvalue(l, lua_upvalueindex(1)); + lua_insert(l, 1); + err = lua_pcall(l, 1, 1, 0); + if (!err) + return 1; + + if (err == LUA_ERRRUN) { + lua_pushnil(l); + lua_insert(l, -2); + return 2; + } + + /* Since we are not using a custom error handler, the only remaining + * errors are memory related */ + return luaL_error(l, "Memory allocation error in CJSON protected call"); +} + +/* Return cjson module table */ +static int lua_cjson_new(lua_State *l) +{ + luaL_Reg reg[] = { + { "encode", json_encode }, + { "decode", json_decode }, + { "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object }, + { "encode_sparse_array", json_cfg_encode_sparse_array }, + { "encode_max_depth", json_cfg_encode_max_depth }, + { "decode_max_depth", json_cfg_decode_max_depth }, + { "encode_number_precision", json_cfg_encode_number_precision }, + { "encode_keep_buffer", json_cfg_encode_keep_buffer }, + { "encode_invalid_numbers", json_cfg_encode_invalid_numbers }, + { "decode_invalid_numbers", json_cfg_decode_invalid_numbers }, + { "new", lua_cjson_new }, + { NULL, NULL } + }; + + /* Initialise number conversions */ + fpconv_init(); + + /* cjson module table */ + lua_newtable(l); + + /* Register functions with config data as upvalue */ + json_create_config(l); + luaL_setfuncs(l, reg, 1); + + /* Set cjson.null */ + lua_pushlightuserdata(l, NULL); + lua_setfield(l, -2, "null"); + + /* Set module name / version fields */ + lua_pushliteral(l, CJSON_MODNAME); + lua_setfield(l, -2, "_NAME"); + lua_pushliteral(l, CJSON_VERSION); + lua_setfield(l, -2, "_VERSION"); + + return 1; +} + +/* Return cjson.safe module table */ +static int lua_cjson_safe_new(lua_State *l) +{ + const char *func[] = { "decode", "encode", NULL }; + int i; + + lua_cjson_new(l); + + /* Fix new() method */ + lua_pushcfunction(l, lua_cjson_safe_new); + lua_setfield(l, -2, "new"); + + for (i = 0; func[i]; i++) { + lua_getfield(l, -1, func[i]); + lua_pushcclosure(l, json_protect_conversion, 1); + lua_setfield(l, -2, func[i]); + } + + return 1; +} + +int luaopen_cjson(lua_State *l) +{ + lua_cjson_new(l); + +#ifdef ENABLE_CJSON_GLOBAL + /* Register a global "cjson" table. */ + lua_pushvalue(l, -1); + lua_setglobal(l, CJSON_MODNAME); +#endif + + /* Return cjson table */ + return 1; +} + +int luaopen_cjson_safe(lua_State *l) +{ + lua_cjson_safe_new(l); + + /* Return cjson.safe table */ + return 1; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/luaclib/cjson/manual.txt b/luaclib/cjson/manual.txt new file mode 100644 index 0000000..a12e378 --- /dev/null +++ b/luaclib/cjson/manual.txt @@ -0,0 +1,612 @@ += Lua CJSON 2.1devel Manual = +Mark Pulford +:revdate: 1st March 2012 + +Overview +-------- + +The Lua CJSON module provides JSON support for Lua. + +*Features*:: +- Fast, standards compliant encoding/parsing routines +- Full support for JSON with UTF-8, including decoding surrogate pairs +- Optional run-time support for common exceptions to the JSON + specification (infinity, NaN,..) +- No dependencies on other libraries + +*Caveats*:: +- UTF-16 and UTF-32 are not supported + +Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for +details. + +The latest version of this software is available from the +http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CJSON website]. + +Feel free to email me if you have any patches, suggestions, or comments. + + +Installation +------------ + +Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, or +http://www.luajit.org[LuaJIT] to build. + +The build method can be selected from 4 options: + +Make:: Unix (including Linux, BSD, Mac OSX & Solaris), Windows +CMake:: Unix, Windows +RPM:: Linux +LuaRocks:: Unix, Windows + + +Make +~~~~ + +The included +Makefile+ has generic settings. + +First, review and update the included makefile to suit your platform (if +required). + +Next, build and install the module: + +[source,sh] +make install + +Or install manually into your Lua module directory: + +[source,sh] +make +cp cjson.so $LUA_MODULE_DIRECTORY + + +CMake +~~~~~ + +http://www.cmake.org[CMake] can generate build configuration for many +different platforms (including Unix and Windows). + +First, generate the makefile for your platform using CMake. If CMake is +unable to find Lua, manually set the +LUA_DIR+ environment variable to +the base prefix of your Lua 5.1 installation. + +While +cmake+ is used in the example below, +ccmake+ or +cmake-gui+ may +be used to present an interface for changing the default build options. + +[source,sh] +mkdir build +cd build +# Optional: export LUA_DIR=$LUA51_PREFIX +cmake .. + +Next, build and install the module: + +[source,sh] +make install +# Or: +make +cp cjson.so $LUA_MODULE_DIRECTORY + +Review the +http://www.cmake.org/cmake/help/documentation.html[CMake documentation] +for further details. + + +RPM +~~~ + +Linux distributions using http://rpm.org[RPM] can create a package via +the included RPM spec file. Ensure the +rpm-build+ package (or similar) +has been installed. + +Build and install the module via RPM: + +[source,sh] +rpmbuild -tb lua-cjson-2.1devel.tar.gz +rpm -Uvh $LUA_CJSON_RPM + + +LuaRocks +~~~~~~~~ + +http://luarocks.org[LuaRocks] can be used to install and manage Lua +modules on a wide range of platforms (including Windows). + +First, extract the Lua CJSON source package. + +Next, install the module: + +[source,sh] +cd lua-cjson-2.1devel +luarocks make + +[NOTE] +LuaRocks does not support platform specific configuration for Solaris. +On Solaris, you may need to manually uncomment +USE_INTERNAL_ISINF+ in +the rockspec before building this module. + +Review the http://luarocks.org/en/Documentation[LuaRocks documentation] +for further details. + + +[[build_options]] +Build Options (#define) +~~~~~~~~~~~~~~~~~~~~~~~ + +Lua CJSON offers several +#define+ build options to address portability +issues, and enable non-default features. Some build methods may +automatically set platform specific options if required. Other features +should be enabled manually. + +USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing +isinf+. +DISABLE_INVALID_NUMBERS:: Recommended on platforms where +strtod+ / + +sprintf+ are not POSIX compliant (eg, Windows MinGW). Prevents + +cjson.encode_invalid_numbers+ and +cjson.decode_invalid_numbers+ from + being enabled. However, +cjson.encode_invalid_numbers+ may still be + set to +"null"+. When using the Lua CJSON built-in floating point + conversion this option is unnecessary and is ignored. + + +Built-in floating point conversion +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Lua CJSON may be built with David Gay's +http://www.netlib.org/fp/[floating point conversion routines]. This can +increase overall performance by up to 50% on some platforms when +converting a large amount of numeric data. However, this option reduces +portability and is disabled by default. + +USE_INTERNAL_FPCONV:: Enable internal number conversion routines. +IEEE_BIG_ENDIAN:: Must be set on big endian architectures. +MULTIPLE_THREADS:: Must be set if Lua CJSON may be used in a + multi-threaded application. Requires the _pthreads_ library. + + +API (Functions) +--------------- + +Synopsis +~~~~~~~~ + +[source,lua] +------------ +-- Module instantiation +local cjson = require "cjson" +local cjson2 = cjson.new() +local cjson_safe = require "cjson.safe" + +-- Translate Lua value to/from JSON +text = cjson.encode(value) +value = cjson.decode(text) + +-- Get and/or set Lua CJSON configuration +setting = cjson.decode_invalid_numbers([setting]) +setting = cjson.encode_invalid_numbers([setting]) +keep = cjson.encode_keep_buffer([keep]) +depth = cjson.encode_max_depth([depth]) +depth = cjson.decode_max_depth([depth]) +convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) +------------ + + +Module Instantiation +~~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +local cjson = require "cjson" +local cjson2 = cjson.new() +local cjson_safe = require "cjson.safe" +------------ + +Import Lua CJSON via the Lua +require+ function. Lua CJSON does not +register a global module table. + +The +cjson+ module will throw an error during JSON conversion if any +invalid data is encountered. Refer to <> +and <> for details. + +The +cjson.safe+ module behaves identically to the +cjson+ module, +except when errors are encountered during JSON conversion. On error, the ++cjson_safe.encode+ and +cjson_safe.decode+ functions will return ++nil+ followed by the error message. + ++cjson.new+ can be used to instantiate an independent copy of the Lua +CJSON module. The new module has a separate persistent encoding buffer, +and default settings. + +Lua CJSON can support Lua implementations using multiple preemptive +threads within a single Lua state provided the persistent encoding +buffer is not shared. This can be achieved by one of the following +methods: + +- Disabling the persistent encoding buffer with + <> +- Ensuring each thread calls <> separately (ie, + treat +cjson.encode+ as non-reentrant). +- Using a separate +cjson+ module table per preemptive thread + (+cjson.new+) + +[NOTE] +Lua CJSON uses +strtod+ and +snprintf+ to perform numeric conversion as +they are usually well supported, fast and bug free. However, these +functions require a workaround for JSON encoding/parsing under locales +using a comma decimal separator. Lua CJSON detects the current locale +during instantiation to determine and automatically implement the +workaround if required. Lua CJSON should be reinitialised via ++cjson.new+ if the locale of the current process changes. Using a +different locale per thread is not supported. + + +decode +~~~~~~ + +[source,lua] +------------ +value = cjson.decode(json_text) +------------ + ++cjson.decode+ will deserialise any UTF-8 JSON string into a Lua value +or table. + +UTF-16 and UTF-32 JSON strings are not supported. + ++cjson.decode+ requires that any NULL (ASCII 0) and double quote (ASCII +34) characters are escaped within strings. All escape codes will be +decoded and other bytes will be passed transparently. UTF-8 characters +are not validated during decoding and should be checked elsewhere if +required. + +JSON +null+ will be converted to a NULL +lightuserdata+ value. This can +be compared with +cjson.null+ for convenience. + +By default, numbers incompatible with the JSON specification (infinity, +NaN, hexadecimal) can be decoded. This default can be changed with +<>. + +.Example: Decoding +[source,lua] +json_text = '[ true, { "foo": "bar" } ]' +value = cjson.decode(json_text) +-- Returns: { true, { foo = "bar" } } + +[CAUTION] +Care must be taken after decoding JSON objects with numeric keys. Each +numeric key will be stored as a Lua +string+. Any subsequent code +assuming type +number+ may break. + + +[[decode_invalid_numbers]] +decode_invalid_numbers +~~~~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +setting = cjson.decode_invalid_numbers([setting]) +-- "setting" must be a boolean. Default: true. +------------ + +Lua CJSON may generate an error when trying to decode numbers not +supported by the JSON specification. _Invalid numbers_ are defined as: + +- infinity +- NaN +- hexadecimal + +Available settings: + ++true+:: Accept and decode _invalid numbers_. This is the default + setting. ++false+:: Throw an error when _invalid numbers_ are encountered. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[decode_max_depth]] +decode_max_depth +~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +depth = cjson.decode_max_depth([depth]) +-- "depth" must be a positive integer. Default: 1000. +------------ + +Lua CJSON will generate an error when parsing deeply nested JSON once +the maximum array/object depth has been exceeded. This check prevents +unnecessarily complicated JSON from slowing down the application, or +crashing the application due to lack of process stack space. + +An error may be generated before the depth limit is hit if Lua is unable +to allocate more objects on the Lua stack. + +By default, Lua CJSON will reject JSON with arrays and/or objects nested +more than 1000 levels deep. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode]] +encode +~~~~~~ + +[source,lua] +------------ +json_text = cjson.encode(value) +------------ + ++cjson.encode+ will serialise a Lua value into a string containing the +JSON representation. + ++cjson.encode+ supports the following types: + +- +boolean+ +- +lightuserdata+ (NULL value only) +- +nil+ +- +number+ +- +string+ +- +table+ + +The remaining Lua types will generate an error: + +- +function+ +- +lightuserdata+ (non-NULL values) +- +thread+ +- +userdata+ + +By default, numbers are encoded with 14 significant digits. Refer to +<> for details. + +Lua CJSON will escape the following characters within each UTF-8 string: + +- Control characters (ASCII 0 - 31) +- Double quote (ASCII 34) +- Forward slash (ASCII 47) +- Blackslash (ASCII 92) +- Delete (ASCII 127) + +All other bytes are passed transparently. + +[CAUTION] +========= +Lua CJSON will successfully encode/decode binary strings, but this is +technically not supported by JSON and may not be compatible with other +JSON libraries. To ensure the output is valid JSON, applications should +ensure all Lua strings passed to +cjson.encode+ are UTF-8. + +Base64 is commonly used to encode binary data as the most efficient +encoding under UTF-8 can only reduce the encoded size by a further +~8%. Lua Base64 routines can be found in the +http://w3.impa.br/%7Ediego/software/luasocket/[LuaSocket] and +http://www.tecgraf.puc-rio.br/%7Elhf/ftp/lua/#lbase64[lbase64] packages. +========= + +Lua CJSON uses a heuristic to determine whether to encode a Lua table as +a JSON array or an object. A Lua table with only positive integer keys +of type +number+ will be encoded as a JSON array. All other tables will +be encoded as a JSON object. + +Lua CJSON does not use metamethods when serialising tables. + +- +rawget+ is used to iterate over Lua arrays +- +next+ is used to iterate over Lua objects + +Lua arrays with missing entries (_sparse arrays_) may optionally be +encoded in several different ways. Refer to +<> for details. + +JSON object keys are always strings. Hence +cjson.encode+ only supports +table keys which are type +number+ or +string+. All other types will +generate an error. + +[NOTE] +Standards compliant JSON must be encapsulated in either an object (+{}+) +or an array (+[]+). If strictly standards compliant JSON is desired, a +table must be passed to +cjson.encode+. + +By default, encoding the following Lua values will generate errors: + +- Numbers incompatible with the JSON specification (infinity, NaN) +- Tables nested more than 1000 levels deep +- Excessively sparse Lua arrays + +These defaults can be changed with: + +- <> +- <> +- <> + +.Example: Encoding +[source,lua] +value = { true, { foo = "bar" } } +json_text = cjson.encode(value) +-- Returns: '[true,{"foo":"bar"}]' + + +[[encode_invalid_numbers]] +encode_invalid_numbers +~~~~~~~~~~~~~~~~~~~~~~ +[source,lua] +------------ +setting = cjson.encode_invalid_numbers([setting]) +-- "setting" must a boolean or "null". Default: false. +------------ + +Lua CJSON may generate an error when encoding floating point numbers not +supported by the JSON specification (_invalid numbers_): + +- infinity +- NaN + +Available settings: + ++true+:: Allow _invalid numbers_ to be encoded using the Javascript + compatible values +NaN+ and +Infinity+. This will generate + non-standard JSON, but these values are supported by some libraries. ++"null"+:: Encode _invalid numbers_ as a JSON +null+ value. This allows + infinity and NaN to be encoded into valid JSON. ++false+:: Throw an error when attempting to encode _invalid numbers_. + This is the default setting. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode_keep_buffer]] +encode_keep_buffer +~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +keep = cjson.encode_keep_buffer([keep]) +-- "keep" must be a boolean. Default: true. +------------ + +Lua CJSON can reuse the JSON encoding buffer to improve performance. + +Available settings: + ++true+:: The buffer will grow to the largest size required and is not + freed until the Lua CJSON module is garbage collected. This is the + default setting. ++false+:: Free the encode buffer after each call to +cjson.encode+. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode_max_depth]] +encode_max_depth +~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +depth = cjson.encode_max_depth([depth]) +-- "depth" must be a positive integer. Default: 1000. +------------ + +Once the maximum table depth has been exceeded Lua CJSON will generate +an error. This prevents a deeply nested or recursive data structure from +crashing the application. + +By default, Lua CJSON will generate an error when trying to encode data +structures with more than 1000 nested tables. + +The current setting is always returned, and is only updated when an +argument is provided. + +.Example: Recursive Lua table +[source,lua] +a = {}; a[1] = a + + +[[encode_number_precision]] +encode_number_precision +~~~~~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +precision = cjson.encode_number_precision([precision]) +-- "precision" must be an integer between 1 and 14. Default: 14. +------------ + +The amount of significant digits returned by Lua CJSON when encoding +numbers can be changed to balance accuracy versus performance. For data +structures containing many numbers, setting ++cjson.encode_number_precision+ to a smaller integer, for example +3+, +can improve encoding performance by up to 50%. + +By default, Lua CJSON will output 14 significant digits when converting +a number to text. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode_sparse_array]] +encode_sparse_array +~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) +-- "convert" must be a boolean. Default: false. +-- "ratio" must be a positive integer. Default: 2. +-- "safe" must be a positive integer. Default: 10. +------------ + +Lua CJSON classifies a Lua table into one of three kinds when encoding a +JSON array. This is determined by the number of values missing from the +Lua array as follows: + +Normal:: All values are available. +Sparse:: At least 1 value is missing. +Excessively sparse:: The number of values missing exceeds the configured + ratio. + +Lua CJSON encodes sparse Lua arrays as JSON arrays using JSON +null+ for +the missing entries. + +An array is excessively sparse when all the following conditions are +met: + +- +ratio+ > +0+ +- _maximum_index_ > +safe+ +- _maximum_index_ > _item_count_ * +ratio+ + +Lua CJSON will never consider an array to be _excessively sparse_ when ++ratio+ = +0+. The +safe+ limit ensures that small Lua arrays are always +encoded as sparse arrays. + +By default, attempting to encode an _excessively sparse_ array will +generate an error. If +convert+ is set to +true+, _excessively sparse_ +arrays will be converted to a JSON object. + +The current settings are always returned. A particular setting is only +changed when the argument is provided (non-++nil++). + +.Example: Encoding a sparse array +[source,lua] +cjson.encode({ [3] = "data" }) +-- Returns: '[null,null,"data"]' + +.Example: Enabling conversion to a JSON object +[source,lua] +cjson.encode_sparse_array(true) +cjson.encode({ [1000] = "excessively sparse" }) +-- Returns: '{"1000":"excessively sparse"}' + + +API (Variables) +--------------- + +_NAME +~~~~~ + +The name of the Lua CJSON module (+"cjson"+). + + +_VERSION +~~~~~~~~ + +The version number of the Lua CJSON module (+"2.1devel"+). + + +null +~~~~ + +Lua CJSON decodes JSON +null+ as a Lua +lightuserdata+ NULL pointer. ++cjson.null+ is provided for comparison. + + +[sect1] +References +---------- + +- http://tools.ietf.org/html/rfc4627[RFC 4627] +- http://www.json.org/[JSON website] + + +// vi:ft=asciidoc tw=72: diff --git a/luaclib/cjson/performance.txt b/luaclib/cjson/performance.txt new file mode 100644 index 0000000..fc3a5bb --- /dev/null +++ b/luaclib/cjson/performance.txt @@ -0,0 +1,89 @@ +JSON module performance comparison under Lua +============================================ +Mark Pulford +:revdate: January 22, 2012 + +This performance comparison aims to provide a guide of relative +performance between several fast and popular JSON modules. + +The examples used in this comparison were mostly sourced from the +http://json.org[JSON website] and +http://tools.ietf.org/html/rfc4627[RFC 4627]. + +Performance will vary widely between platforms and data sets. These +results should only be used as an approximation. + + +Modules +------- + +The following JSON modules for Lua were tested: + +http://chiselapp.com/user/dhkolf/repository/dkjson/[DKJSON 2.1]:: + - Lua implementation with no dependencies on other libraries + - Supports LPeg to improve decode performance + +https://github.com/brimworks/lua-yajl[Lua YAJL 2.0]:: + - C wrapper for the YAJL library + +http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CSJON 2.0.0]:: + - C implementation with no dependencies on other libraries + + +Summary +------- + +All modules were built and tested as follows: + +DKJSON:: Tested with/without LPeg 10.2. +Lua YAJL:: Tested with YAJL 2.0.4. +Lua CJSON:: Tested with Libc and internal floating point conversion + routines. + +The following Lua implementations were used for this comparison: + +- http://www.lua.org[Lua 5.1.4] (_Lua_) +- http://www.luajit.org[LuaJIT 2.0.0-beta9] (_JIT_) + +These results show the number of JSON operations per second sustained by +each module. All results have been normalised against the pure Lua +DKJSON implementation. + +.Decoding performance +............................................................................ + | DKJSON | Lua YAJL | Lua CJSON + | No LPeg With LPeg | | Libc Internal + | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT +example1 | 1x 2x 2.6x 3.4x | 7.1x 10x | 14x 20x 14x 20x +example2 | 1x 2.2x 2.9x 4.4x | 6.7x 9.9x | 14x 22x 14x 22x +example3 | 1x 2.1x 3x 4.3x | 6.9x 9.3x | 14x 21x 15x 22x +example4 | 1x 2x 2.5x 3.7x | 7.3x 10x | 12x 19x 12x 20x +example5 | 1x 2.2x 3x 4.5x | 7.8x 11x | 16x 24x 16x 24x +numbers | 1x 2.2x 2.3x 4x | 4.6x 5.5x | 8.9x 10x 13x 17x +rfc-example1 | 1x 2.1x 2.8x 4.3x | 6.1x 8.1x | 13x 19x 14x 21x +rfc-example2 | 1x 2.1x 3.1x 4.2x | 7.1x 9.2x | 15x 21x 17x 24x +types | 1x 2.2x 2.6x 4.3x | 5.3x 7.4x | 12x 20x 13x 21x +-------------|-------------------------|------------|----------------------- += Average => | 1x 2.1x 2.7x 4.1x | 6.5x 9x | 13x 20x 14x 21x +............................................................................ + +.Encoding performance +............................................................................. + | DKJSON | Lua YAJL | Lua CJSON + | No LPeg With LPeg | | Libc Internal + | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT +example1 | 1x 1.8x 0.97x 1.6x | 3.1x 5.2x | 23x 29x 23x 29x +example2 | 1x 2x 0.97x 1.7x | 2.6x 4.3x | 22x 28x 22x 28x +example3 | 1x 1.9x 0.98x 1.6x | 2.8x 4.3x | 13x 15x 16x 18x +example4 | 1x 1.7x 0.96x 1.3x | 3.9x 6.1x | 15x 19x 17x 21x +example5 | 1x 2x 0.98x 1.7x | 2.7x 4.5x | 20x 23x 20x 23x +numbers | 1x 2.3x 1x 2.2x | 1.3x 1.9x | 3.8x 4.1x 4.2x 4.6x +rfc-example1 | 1x 1.9x 0.97x 1.6x | 2.2x 3.2x | 8.5x 9.3x 11x 12x +rfc-example2 | 1x 1.9x 0.98x 1.6x | 2.6x 3.9x | 10x 11x 17x 19x +types | 1x 2.2x 0.97x 2x | 1.2x 1.9x | 11x 13x 12x 14x +-------------|-------------------------|------------|----------------------- += Average => | 1x 1.9x 0.98x 1.7x | 2.5x 3.9x | 14x 17x 16x 19x +............................................................................. + + +// vi:ft=asciidoc tw=72: diff --git a/luaclib/cjson/rfc4627.txt b/luaclib/cjson/rfc4627.txt new file mode 100644 index 0000000..67b8909 --- /dev/null +++ b/luaclib/cjson/rfc4627.txt @@ -0,0 +1,563 @@ + + + + + + +Network Working Group D. Crockford +Request for Comments: 4627 JSON.org +Category: Informational July 2006 + + + The application/json Media Type for JavaScript Object Notation (JSON) + +Status of This Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2006). + +Abstract + + JavaScript Object Notation (JSON) is a lightweight, text-based, + language-independent data interchange format. It was derived from + the ECMAScript Programming Language Standard. JSON defines a small + set of formatting rules for the portable representation of structured + data. + +1. Introduction + + JavaScript Object Notation (JSON) is a text format for the + serialization of structured data. It is derived from the object + literals of JavaScript, as defined in the ECMAScript Programming + Language Standard, Third Edition [ECMA]. + + JSON can represent four primitive types (strings, numbers, booleans, + and null) and two structured types (objects and arrays). + + A string is a sequence of zero or more Unicode characters [UNICODE]. + + An object is an unordered collection of zero or more name/value + pairs, where a name is a string and a value is a string, number, + boolean, null, object, or array. + + An array is an ordered sequence of zero or more values. + + The terms "object" and "array" come from the conventions of + JavaScript. + + JSON's design goals were for it to be minimal, portable, textual, and + a subset of JavaScript. + + + +Crockford Informational [Page 1] + +RFC 4627 JSON July 2006 + + +1.1. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The grammatical rules in this document are to be interpreted as + described in [RFC4234]. + +2. JSON Grammar + + A JSON text is a sequence of tokens. The set of tokens includes six + structural characters, strings, numbers, and three literal names. + + A JSON text is a serialized object or array. + + JSON-text = object / array + + These are the six structural characters: + + begin-array = ws %x5B ws ; [ left square bracket + + begin-object = ws %x7B ws ; { left curly bracket + + end-array = ws %x5D ws ; ] right square bracket + + end-object = ws %x7D ws ; } right curly bracket + + name-separator = ws %x3A ws ; : colon + + value-separator = ws %x2C ws ; , comma + + Insignificant whitespace is allowed before or after any of the six + structural characters. + + ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +2.1. Values + + A JSON value MUST be an object, array, number, or string, or one of + the following three literal names: + + false null true + + + +Crockford Informational [Page 2] + +RFC 4627 JSON July 2006 + + + The literal names MUST be lowercase. No other literal names are + allowed. + + value = false / null / true / object / array / number / string + + false = %x66.61.6c.73.65 ; false + + null = %x6e.75.6c.6c ; null + + true = %x74.72.75.65 ; true + +2.2. Objects + + An object structure is represented as a pair of curly brackets + surrounding zero or more name/value pairs (or members). A name is a + string. A single colon comes after each name, separating the name + from the value. A single comma separates a value from a following + name. The names within an object SHOULD be unique. + + object = begin-object [ member *( value-separator member ) ] + end-object + + member = string name-separator value + +2.3. Arrays + + An array structure is represented as square brackets surrounding zero + or more values (or elements). Elements are separated by commas. + + array = begin-array [ value *( value-separator value ) ] end-array + +2.4. Numbers + + The representation of numbers is similar to that used in most + programming languages. A number contains an integer component that + may be prefixed with an optional minus sign, which may be followed by + a fraction part and/or an exponent part. + + Octal and hex forms are not allowed. Leading zeros are not allowed. + + A fraction part is a decimal point followed by one or more digits. + + An exponent part begins with the letter E in upper or lowercase, + which may be followed by a plus or minus sign. The E and optional + sign are followed by one or more digits. + + Numeric values that cannot be represented as sequences of digits + (such as Infinity and NaN) are not permitted. + + + +Crockford Informational [Page 3] + +RFC 4627 JSON July 2006 + + + number = [ minus ] int [ frac ] [ exp ] + + decimal-point = %x2E ; . + + digit1-9 = %x31-39 ; 1-9 + + e = %x65 / %x45 ; e E + + exp = e [ minus / plus ] 1*DIGIT + + frac = decimal-point 1*DIGIT + + int = zero / ( digit1-9 *DIGIT ) + + minus = %x2D ; - + + plus = %x2B ; + + + zero = %x30 ; 0 + +2.5. Strings + + The representation of strings is similar to conventions used in the C + family of programming languages. A string begins and ends with + quotation marks. All Unicode characters may be placed within the + quotation marks except for the characters that must be escaped: + quotation mark, reverse solidus, and the control characters (U+0000 + through U+001F). + + Any character may be escaped. If the character is in the Basic + Multilingual Plane (U+0000 through U+FFFF), then it may be + represented as a six-character sequence: a reverse solidus, followed + by the lowercase letter u, followed by four hexadecimal digits that + encode the character's code point. The hexadecimal letters A though + F can be upper or lowercase. So, for example, a string containing + only a single reverse solidus character may be represented as + "\u005C". + + Alternatively, there are two-character sequence escape + representations of some popular characters. So, for example, a + string containing only a single reverse solidus character may be + represented more compactly as "\\". + + To escape an extended character that is not in the Basic Multilingual + Plane, the character is represented as a twelve-character sequence, + encoding the UTF-16 surrogate pair. So, for example, a string + containing only the G clef character (U+1D11E) may be represented as + "\uD834\uDD1E". + + + +Crockford Informational [Page 4] + +RFC 4627 JSON July 2006 + + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + +3. Encoding + + JSON text SHALL be encoded in Unicode. The default encoding is + UTF-8. + + Since the first two characters of a JSON text will always be ASCII + characters [RFC0020], it is possible to determine whether an octet + stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + at the pattern of nulls in the first four octets. + + 00 00 00 xx UTF-32BE + 00 xx 00 xx UTF-16BE + xx 00 00 00 UTF-32LE + xx 00 xx 00 UTF-16LE + xx xx xx xx UTF-8 + +4. Parsers + + A JSON parser transforms a JSON text into another representation. A + JSON parser MUST accept all texts that conform to the JSON grammar. + A JSON parser MAY accept non-JSON forms or extensions. + + An implementation may set limits on the size of texts that it + accepts. An implementation may set limits on the maximum depth of + nesting. An implementation may set limits on the range of numbers. + An implementation may set limits on the length and character contents + of strings. + + + + +Crockford Informational [Page 5] + +RFC 4627 JSON July 2006 + + +5. Generators + + A JSON generator produces JSON text. The resulting text MUST + strictly conform to the JSON grammar. + +6. IANA Considerations + + The MIME media type for JSON text is application/json. + + Type name: application + + Subtype name: json + + Required parameters: n/a + + Optional parameters: n/a + + Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32 + + JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON + is written in UTF-8, JSON is 8bit compatible. When JSON is + written in UTF-16 or UTF-32, the binary content-transfer-encoding + must be used. + + Security considerations: + + Generally there are security issues with scripting languages. JSON + is a subset of JavaScript, but it is a safe subset that excludes + assignment and invocation. + + A JSON text can be safely passed into JavaScript's eval() function + (which compiles and executes a string) if all the characters not + enclosed in strings are in the set of characters that form JSON + tokens. This can be quickly determined in JavaScript with two + regular expressions and calls to the test and replace methods. + + var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( + text.replace(/"(\\.|[^"\\])*"/g, ''))) && + eval('(' + text + ')'); + + Interoperability considerations: n/a + + Published specification: RFC 4627 + + + + + + + + +Crockford Informational [Page 6] + +RFC 4627 JSON July 2006 + + + Applications that use this media type: + + JSON has been used to exchange data between applications written + in all of these programming languages: ActionScript, C, C#, + ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua, + Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme. + + Additional information: + + Magic number(s): n/a + File extension(s): .json + Macintosh file type code(s): TEXT + + Person & email address to contact for further information: + Douglas Crockford + douglas@crockford.com + + Intended usage: COMMON + + Restrictions on usage: none + + Author: + Douglas Crockford + douglas@crockford.com + + Change controller: + Douglas Crockford + douglas@crockford.com + +7. Security Considerations + + See Security Considerations in Section 6. + +8. Examples + + This is a JSON object: + + { + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + + + +Crockford Informational [Page 7] + +RFC 4627 JSON July 2006 + + + } + } + + Its Image member is an object whose Thumbnail member is an object + and whose IDs member is an array of numbers. + + This is a JSON array containing two objects: + + [ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] + +9. References + +9.1. Normative References + + [ECMA] European Computer Manufacturers Association, "ECMAScript + Language Specification 3rd Edition", December 1999, + . + + [RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20, + October 1969. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", RFC 4234, October 2005. + + + +Crockford Informational [Page 8] + +RFC 4627 JSON July 2006 + + + [UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0", + 2003, . + +Author's Address + + Douglas Crockford + JSON.org + EMail: douglas@crockford.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Crockford Informational [Page 9] + +RFC 4627 JSON July 2006 + + +Full Copyright Statement + + Copyright (C) The Internet Society (2006). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET + ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE + INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is provided by the IETF + Administrative Support Activity (IASA). + + + + + + + +Crockford Informational [Page 10] + diff --git a/luaclib/cjson/runtests.sh b/luaclib/cjson/runtests.sh new file mode 100644 index 0000000..87db0bc --- /dev/null +++ b/luaclib/cjson/runtests.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +PLATFORM="`uname -s`" +[ "$1" ] && VERSION="$1" || VERSION="2.1devel" + +set -e + +# Portable "ggrep -A" replacement. +# Work around Solaris awk record limit of 2559 bytes. +# contextgrep PATTERN POST_MATCH_LINES +contextgrep() { + cut -c -2500 | awk "/$1/ { count = ($2 + 1) } count > 0 { count--; print }" +} + +do_tests() { + echo + cd tests + lua -e 'print("Testing Lua CJSON version " .. require("cjson")._VERSION)' + ./test.lua | contextgrep 'FAIL|Summary' 3 | grep -v PASS | cut -c -150 + cd .. +} + +echo "===== Setting LuaRocks PATH =====" +eval "`luarocks path`" + +echo "===== Building UTF-8 test data =====" +( cd tests && ./genutf8.pl; ) + +echo "===== Cleaning old build data =====" +make clean +rm -f tests/cjson.so + +echo "===== Verifying cjson.so is not installed =====" + +cd tests +if lua -e 'require "cjson"' 2>/dev/null +then + cat < "$LOG" + RPM="`awk '/^Wrote: / && ! /debuginfo/ { print $2}' < "$LOG"`" + sudo -- rpm -Uvh \"$RPM\" + do_tests + sudo -- rpm -e lua-cjson + rm -f "$LOG" + else + echo "==> skipping, $TGZ not found" + fi +fi + +# vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/strbuf.c b/luaclib/cjson/strbuf.c new file mode 100644 index 0000000..f0f7f4b --- /dev/null +++ b/luaclib/cjson/strbuf.c @@ -0,0 +1,251 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford + * + * 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. + */ + +#include +#include +#include +#include + +#include "strbuf.h" + +static void die(const char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); + fprintf(stderr, "\n"); + + exit(-1); +} + +void strbuf_init(strbuf_t *s, int len) +{ + int size; + + if (len <= 0) + size = STRBUF_DEFAULT_SIZE; + else + size = len + 1; /* \0 terminator */ + + s->buf = NULL; + s->size = size; + s->length = 0; + s->increment = STRBUF_DEFAULT_INCREMENT; + s->dynamic = 0; + s->reallocs = 0; + s->debug = 0; + + s->buf = malloc(size); + if (!s->buf) + die("Out of memory"); + + strbuf_ensure_null(s); +} + +strbuf_t *strbuf_new(int len) +{ + strbuf_t *s; + + s = malloc(sizeof(strbuf_t)); + if (!s) + die("Out of memory"); + + strbuf_init(s, len); + + /* Dynamic strbuf allocation / deallocation */ + s->dynamic = 1; + + return s; +} + +void strbuf_set_increment(strbuf_t *s, int increment) +{ + /* Increment > 0: Linear buffer growth rate + * Increment < -1: Exponential buffer growth rate */ + if (increment == 0 || increment == -1) + die("BUG: Invalid string increment"); + + s->increment = increment; +} + +static inline void debug_stats(strbuf_t *s) +{ + if (s->debug) { + fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", + (long)s, s->reallocs, s->length, s->size); + } +} + +/* If strbuf_t has not been dynamically allocated, strbuf_free() can + * be called any number of times strbuf_init() */ +void strbuf_free(strbuf_t *s) +{ + debug_stats(s); + + if (s->buf) { + free(s->buf); + s->buf = NULL; + } + if (s->dynamic) + free(s); +} + +char *strbuf_free_to_string(strbuf_t *s, int *len) +{ + char *buf; + + debug_stats(s); + + strbuf_ensure_null(s); + + buf = s->buf; + if (len) + *len = s->length; + + if (s->dynamic) + free(s); + + return buf; +} + +static int calculate_new_size(strbuf_t *s, int len) +{ + int reqsize, newsize; + + if (len <= 0) + die("BUG: Invalid strbuf length requested"); + + /* Ensure there is room for optional NULL termination */ + reqsize = len + 1; + + /* If the user has requested to shrink the buffer, do it exactly */ + if (s->size > reqsize) + return reqsize; + + newsize = s->size; + if (s->increment < 0) { + /* Exponential sizing */ + while (newsize < reqsize) + newsize *= -s->increment; + } else { + /* Linear sizing */ + newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; + } + + return newsize; +} + + +/* Ensure strbuf can handle a string length bytes long (ignoring NULL + * optional termination). */ +void strbuf_resize(strbuf_t *s, int len) +{ + int newsize; + + newsize = calculate_new_size(s, len); + + if (s->debug > 1) { + fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", + (long)s, s->size, newsize); + } + + s->size = newsize; + s->buf = realloc(s->buf, s->size); + if (!s->buf) + die("Out of memory"); + s->reallocs++; +} + +void strbuf_append_string(strbuf_t *s, const char *str) +{ + int space, i; + + space = strbuf_empty_length(s); + + for (i = 0; str[i]; i++) { + if (space < 1) { + strbuf_resize(s, s->length + 1); + space = strbuf_empty_length(s); + } + + s->buf[s->length] = str[i]; + s->length++; + space--; + } +} + +/* strbuf_append_fmt() should only be used when an upper bound + * is known for the output string. */ +void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) +{ + va_list arg; + int fmt_len; + + strbuf_ensure_empty_length(s, len); + + va_start(arg, fmt); + fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); + va_end(arg); + + if (fmt_len < 0) + die("BUG: Unable to convert number"); /* This should never happen.. */ + + s->length += fmt_len; +} + +/* strbuf_append_fmt_retry() can be used when the there is no known + * upper bound for the output string. */ +void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) +{ + va_list arg; + int fmt_len, try; + int empty_len; + + /* If the first attempt to append fails, resize the buffer appropriately + * and try again */ + for (try = 0; ; try++) { + va_start(arg, fmt); + /* Append the new formatted string */ + /* fmt_len is the length of the string required, excluding the + * trailing NULL */ + empty_len = strbuf_empty_length(s); + /* Add 1 since there is also space to store the terminating NULL. */ + fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); + va_end(arg); + + if (fmt_len <= empty_len) + break; /* SUCCESS */ + if (try > 0) + die("BUG: length of formatted string changed"); + + strbuf_resize(s, s->length + fmt_len); + } + + s->length += fmt_len; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/luaclib/cjson/strbuf.h b/luaclib/cjson/strbuf.h new file mode 100644 index 0000000..d861108 --- /dev/null +++ b/luaclib/cjson/strbuf.h @@ -0,0 +1,154 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford + * + * 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. + */ + +#include +#include + +/* Size: Total bytes allocated to *buf + * Length: String length, excluding optional NULL terminator. + * Increment: Allocation increments when resizing the string buffer. + * Dynamic: True if created via strbuf_new() + */ + +typedef struct { + char *buf; + int size; + int length; + int increment; + int dynamic; + int reallocs; + int debug; +} strbuf_t; + +#ifndef STRBUF_DEFAULT_SIZE +#define STRBUF_DEFAULT_SIZE 1023 +#endif +#ifndef STRBUF_DEFAULT_INCREMENT +#define STRBUF_DEFAULT_INCREMENT -2 +#endif + +/* Initialise */ +extern strbuf_t *strbuf_new(int len); +extern void strbuf_init(strbuf_t *s, int len); +extern void strbuf_set_increment(strbuf_t *s, int increment); + +/* Release */ +extern void strbuf_free(strbuf_t *s); +extern char *strbuf_free_to_string(strbuf_t *s, int *len); + +/* Management */ +extern void strbuf_resize(strbuf_t *s, int len); +static int strbuf_empty_length(strbuf_t *s); +static int strbuf_length(strbuf_t *s); +static char *strbuf_string(strbuf_t *s, int *len); +static void strbuf_ensure_empty_length(strbuf_t *s, int len); +static char *strbuf_empty_ptr(strbuf_t *s); +static void strbuf_extend_length(strbuf_t *s, int len); + +/* Update */ +extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); +extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); +static void strbuf_append_mem(strbuf_t *s, const char *c, int len); +extern void strbuf_append_string(strbuf_t *s, const char *str); +static void strbuf_append_char(strbuf_t *s, const char c); +static void strbuf_ensure_null(strbuf_t *s); + +/* Reset string for before use */ +static inline void strbuf_reset(strbuf_t *s) +{ + s->length = 0; +} + +static inline int strbuf_allocated(strbuf_t *s) +{ + return s->buf != NULL; +} + +/* Return bytes remaining in the string buffer + * Ensure there is space for a NULL terminator. */ +static inline int strbuf_empty_length(strbuf_t *s) +{ + return s->size - s->length - 1; +} + +static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) +{ + if (len > strbuf_empty_length(s)) + strbuf_resize(s, s->length + len); +} + +static inline char *strbuf_empty_ptr(strbuf_t *s) +{ + return s->buf + s->length; +} + +static inline void strbuf_extend_length(strbuf_t *s, int len) +{ + s->length += len; +} + +static inline int strbuf_length(strbuf_t *s) +{ + return s->length; +} + +static inline void strbuf_append_char(strbuf_t *s, const char c) +{ + strbuf_ensure_empty_length(s, 1); + s->buf[s->length++] = c; +} + +static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) +{ + s->buf[s->length++] = c; +} + +static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) +{ + strbuf_ensure_empty_length(s, len); + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) +{ + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_ensure_null(strbuf_t *s) +{ + s->buf[s->length] = 0; +} + +static inline char *strbuf_string(strbuf_t *s, int *len) +{ + if (len) + *len = s->length; + + return s->buf; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/luaclib/cjson/tests/README b/luaclib/cjson/tests/README new file mode 100644 index 0000000..39e8bd4 --- /dev/null +++ b/luaclib/cjson/tests/README @@ -0,0 +1,4 @@ +These JSON examples were taken from the JSON website +(http://json.org/example.html) and RFC 4627. + +Used with permission. diff --git a/luaclib/cjson/tests/TestLua.pm b/luaclib/cjson/tests/TestLua.pm new file mode 100644 index 0000000..cd22d83 --- /dev/null +++ b/luaclib/cjson/tests/TestLua.pm @@ -0,0 +1,70 @@ +package TestLua; + +use Test::Base -Base; +use IPC::Run3; +use Cwd; + +use Test::LongString; + +our @EXPORT = qw( run_tests ); + +$ENV{LUA_CPATH} = "../?.so;;"; +$ENV{LUA_PATH} = "../lua/?.lua;;"; +#$ENV{LUA_PATH} = ($ENV{LUA_PATH} || "" ) . ';' . getcwd . "/runtime/?.lua" . ';;'; + +sub run_test ($) { + my $block = shift; + #print $json_xs->pretty->encode(\@new_rows); + #my $res = #print $json_xs->pretty->encode($res); + my $name = $block->name; + + my $lua = $block->lua or + die "No --- lua specified for test $name\n"; + + my $luafile = "test_case.lua"; + + open my $fh, ">$luafile" or + die "Cannot open $luafile for writing: $!\n"; + + print $fh $lua; + close $fh; + + my ($res, $err); + + my @cmd; + + if ($ENV{TEST_LUA_USE_VALGRIND}) { + @cmd = ('valgrind', '-q', '--leak-check=full', 'lua', 'test_case.lua'); + } else { + @cmd = ('lua', 'test_case.lua'); + } + + run3 \@cmd, undef, \$res, \$err; + my $rc = $?; + + #warn "res:$res\nerr:$err\n"; + + if (defined $block->err) { + $err =~ /.*:.*:.*: (.*\s)?/; + $err = $1; + is $err, $block->err, "$name - err expected"; + + } elsif ($rc) { + die "Failed to execute --- lua for test $name: $err\n"; + + } else { + #is $res, $block->out, "$name - output ok"; + is $res, $block->out, "$name - output ok"; + } + + is $rc, ($block->exit || 0), "$name - exit code ok"; + #unlink 'test_case.lua' or warn "could not delete \'test_case.lua\':$!"; +} + +sub run_tests () { + for my $block (blocks()) { + run_test($block); + } +} + +1; diff --git a/luaclib/cjson/tests/agentzh.t b/luaclib/cjson/tests/agentzh.t new file mode 100644 index 0000000..e65288f --- /dev/null +++ b/luaclib/cjson/tests/agentzh.t @@ -0,0 +1,52 @@ +use TestLua; + +plan tests => 2 * blocks(); + +run_tests(); + +__DATA__ + +=== TEST 1: empty tables as objects +--- lua +local cjson = require "cjson" +print(cjson.encode({})) +print(cjson.encode({dogs = {}})) +--- out +{} +{"dogs":{}} + + + +=== TEST 2: empty tables as arrays +--- lua +local cjson = require "cjson" +cjson.encode_empty_table_as_object(false) +print(cjson.encode({})) +print(cjson.encode({dogs = {}})) +--- out +[] +{"dogs":[]} + + + +=== TEST 3: empty tables as objects (explicit) +--- lua +local cjson = require "cjson" +cjson.encode_empty_table_as_object(true) +print(cjson.encode({})) +print(cjson.encode({dogs = {}})) +--- out +{} +{"dogs":{}} + + + +=== TEST 4: & in JSON +--- lua +local cjson = require "cjson" +local a="[\"a=1&b=2\"]" +local b=cjson.decode(a) +print(cjson.encode(b)) +--- out +["a=1&b=2"] + diff --git a/luaclib/cjson/tests/bench.lua b/luaclib/cjson/tests/bench.lua new file mode 100644 index 0000000..648020b --- /dev/null +++ b/luaclib/cjson/tests/bench.lua @@ -0,0 +1,131 @@ +#!/usr/bin/env lua + +-- This benchmark script measures wall clock time and should be +-- run on an unloaded system. +-- +-- Your Mileage May Vary. +-- +-- Mark Pulford + +local json_module = os.getenv("JSON_MODULE") or "cjson" + +require "socket" +local json = require(json_module) +local util = require "cjson.util" + +local function find_func(mod, funcnames) + for _, v in ipairs(funcnames) do + if mod[v] then + return mod[v] + end + end + + return nil +end + +local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" }) +local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" }) + +local function average(t) + local total = 0 + for _, v in ipairs(t) do + total = total + v + end + return total / #t +end + +function benchmark(tests, seconds, rep) + local function bench(func, iter) + -- Use socket.gettime() to measure microsecond resolution + -- wall clock time. + local t = socket.gettime() + for i = 1, iter do + func(i) + end + t = socket.gettime() - t + + -- Don't trust any results when the run lasted for less than a + -- millisecond - return nil. + if t < 0.001 then + return nil + end + + return (iter / t) + end + + -- Roughly calculate the number of interations required + -- to obtain a particular time period. + local function calc_iter(func, seconds) + local iter = 1 + local rate + -- Warm up the bench function first. + func() + while not rate do + rate = bench(func, iter) + iter = iter * 10 + end + return math.ceil(seconds * rate) + end + + local test_results = {} + for name, func in pairs(tests) do + -- k(number), v(string) + -- k(string), v(function) + -- k(number), v(function) + if type(func) == "string" then + name = func + func = _G[name] + end + + local iter = calc_iter(func, seconds) + + local result = {} + for i = 1, rep do + result[i] = bench(func, iter) + end + + -- Remove the slowest half (round down) of the result set + table.sort(result) + for i = 1, math.floor(#result / 2) do + table.remove(result, 1) + end + + test_results[name] = average(result) + end + + return test_results +end + +function bench_file(filename) + local data_json = util.file_load(filename) + local data_obj = json_decode(data_json) + + local function test_encode() + json_encode(data_obj) + end + local function test_decode() + json_decode(data_json) + end + + local tests = {} + if json_encode then tests.encode = test_encode end + if json_decode then tests.decode = test_decode end + + return benchmark(tests, 0.1, 5) +end + +-- Optionally load any custom configuration required for this module +local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module)) +if success then + util.run_script(data, _G) + configure(json) +end + +for i = 1, #arg do + local results = bench_file(arg[i]) + for k, v in pairs(results) do + print(("%s\t%s\t%d"):format(arg[i], k, v)) + end +end + +-- vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/tests/example1.json b/luaclib/cjson/tests/example1.json new file mode 100644 index 0000000..42486ce --- /dev/null +++ b/luaclib/cjson/tests/example1.json @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Mark up Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/luaclib/cjson/tests/example2.json b/luaclib/cjson/tests/example2.json new file mode 100644 index 0000000..5600991 --- /dev/null +++ b/luaclib/cjson/tests/example2.json @@ -0,0 +1,11 @@ +{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } +}} diff --git a/luaclib/cjson/tests/example3.json b/luaclib/cjson/tests/example3.json new file mode 100644 index 0000000..d7237a5 --- /dev/null +++ b/luaclib/cjson/tests/example3.json @@ -0,0 +1,26 @@ +{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/luaclib/cjson/tests/example4.json b/luaclib/cjson/tests/example4.json new file mode 100644 index 0000000..d31a395 --- /dev/null +++ b/luaclib/cjson/tests/example4.json @@ -0,0 +1,88 @@ +{"web-app": { + "servlet": [ + { + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500}}, + { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2"}}, + { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet"}, + + { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet"}, + { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true}}], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*"}, + + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} diff --git a/luaclib/cjson/tests/example5.json b/luaclib/cjson/tests/example5.json new file mode 100644 index 0000000..49980ca --- /dev/null +++ b/luaclib/cjson/tests/example5.json @@ -0,0 +1,27 @@ +{"menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + null, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + null, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."} + ] +}} diff --git a/luaclib/cjson/tests/genutf8.pl b/luaclib/cjson/tests/genutf8.pl new file mode 100644 index 0000000..db661a1 --- /dev/null +++ b/luaclib/cjson/tests/genutf8.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl + +# Create test comparison data using a different UTF-8 implementation. + +# The generated utf8.dat file must have the following MD5 sum: +# cff03b039d850f370a7362f3313e5268 + +use strict; + +# 0xD800 - 0xDFFF are used to encode supplementary codepoints +# 0x10000 - 0x10FFFF are supplementary codepoints +my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF); + +my $utf8 = pack("U*", @codepoints); +defined($utf8) or die "Unable create UTF-8 string\n"; + +open(FH, ">:utf8", "utf8.dat") + or die "Unable to open utf8.dat: $!\n"; +print FH $utf8 + or die "Unable to write utf8.dat\n"; +close(FH); + +# vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/tests/numbers.json b/luaclib/cjson/tests/numbers.json new file mode 100644 index 0000000..4f981ff --- /dev/null +++ b/luaclib/cjson/tests/numbers.json @@ -0,0 +1,7 @@ +[ 0.110001, + 0.12345678910111, + 0.412454033640, + 2.6651441426902, + 2.718281828459, + 3.1415926535898, + 2.1406926327793 ] diff --git a/luaclib/cjson/tests/octets-escaped.dat b/luaclib/cjson/tests/octets-escaped.dat new file mode 100644 index 0000000..ee99a6b --- /dev/null +++ b/luaclib/cjson/tests/octets-escaped.dat @@ -0,0 +1 @@ +"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-.\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f" \ No newline at end of file diff --git a/luaclib/cjson/tests/rfc-example1.json b/luaclib/cjson/tests/rfc-example1.json new file mode 100644 index 0000000..73532fa --- /dev/null +++ b/luaclib/cjson/tests/rfc-example1.json @@ -0,0 +1,13 @@ +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + } +} diff --git a/luaclib/cjson/tests/rfc-example2.json b/luaclib/cjson/tests/rfc-example2.json new file mode 100644 index 0000000..2a0cb68 --- /dev/null +++ b/luaclib/cjson/tests/rfc-example2.json @@ -0,0 +1,22 @@ +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } +] diff --git a/luaclib/cjson/tests/test.lua b/luaclib/cjson/tests/test.lua new file mode 100644 index 0000000..b8fce84 --- /dev/null +++ b/luaclib/cjson/tests/test.lua @@ -0,0 +1,425 @@ +#!/usr/bin/env lua + +-- Lua CJSON tests +-- +-- Mark Pulford +-- +-- Note: The output of this script is easier to read with "less -S" + +local json = require "cjson" +local json_safe = require "cjson.safe" +local util = require "cjson.util" + +local function gen_raw_octets() + local chars = {} + for i = 0, 255 do chars[i + 1] = string.char(i) end + return table.concat(chars) +end + +-- Generate every UTF-16 codepoint, including supplementary codes +local function gen_utf16_escaped() + -- Create raw table escapes + local utf16_escaped = {} + local count = 0 + + local function append_escape(code) + local esc = ('\\u%04X'):format(code) + table.insert(utf16_escaped, esc) + end + + table.insert(utf16_escaped, '"') + for i = 0, 0xD7FF do + append_escape(i) + end + -- Skip 0xD800 - 0xDFFF since they are used to encode supplementary + -- codepoints + for i = 0xE000, 0xFFFF do + append_escape(i) + end + -- Append surrogate pair for each supplementary codepoint + for high = 0xD800, 0xDBFF do + for low = 0xDC00, 0xDFFF do + append_escape(high) + append_escape(low) + end + end + table.insert(utf16_escaped, '"') + + return table.concat(utf16_escaped) +end + +function load_testdata() + local data = {} + + -- Data for 8bit raw <-> escaped octets tests + data.octets_raw = gen_raw_octets() + data.octets_escaped = util.file_load("octets-escaped.dat") + + -- Data for \uXXXX -> UTF-8 test + data.utf16_escaped = gen_utf16_escaped() + + -- Load matching data for utf16_escaped + local utf8_loaded + utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat") + if not utf8_loaded then + data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl" + end + + data.table_cycle = {} + data.table_cycle[1] = data.table_cycle + + local big = {} + for i = 1, 1100 do + big = { { 10, false, true, json.null }, "string", a = big } + end + data.deeply_nested_data = big + + return data +end + +function test_decode_cycle(filename) + local obj1 = json.decode(util.file_load(filename)) + local obj2 = json.decode(json.encode(obj1)) + return util.compare_values(obj1, obj2) +end + +-- Set up data used in tests +local Inf = math.huge; +local NaN = math.huge * 0; + +local testdata = load_testdata() + +local cjson_tests = { + -- Test API variables + { "Check module name, version", + function () return json._NAME, json._VERSION end, { }, + true, { "cjson", "2.1devel" } }, + + -- Test decoding simple types + { "Decode string", + json.decode, { '"test string"' }, true, { "test string" } }, + { "Decode numbers", + json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' }, + true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } }, + { "Decode null", + json.decode, { 'null' }, true, { json.null } }, + { "Decode true", + json.decode, { 'true' }, true, { true } }, + { "Decode false", + json.decode, { 'false' }, true, { false } }, + { "Decode object with numeric keys", + json.decode, { '{ "1": "one", "3": "three" }' }, + true, { { ["1"] = "one", ["3"] = "three" } } }, + { "Decode object with string keys", + json.decode, { '{ "a": "a", "b": "b" }' }, + true, { { a = "a", b = "b" } } }, + { "Decode array", + json.decode, { '[ "one", null, "three" ]' }, + true, { { "one", json.null, "three" } } }, + + -- Test decoding errors + { "Decode UTF-16BE [throw error]", + json.decode, { '\0"\0"' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode UTF-16LE [throw error]", + json.decode, { '"\0"\0' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode UTF-32BE [throw error]", + json.decode, { '\0\0\0"' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode UTF-32LE [throw error]", + json.decode, { '"\0\0\0' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode partial JSON [throw error]", + json.decode, { '{ "unexpected eof": ' }, + false, { "Expected value but found T_END at character 21" } }, + { "Decode with extra comma [throw error]", + json.decode, { '{ "extra data": true }, false' }, + false, { "Expected the end but found T_COMMA at character 23" } }, + { "Decode invalid escape code [throw error]", + json.decode, { [[ { "bad escape \q code" } ]] }, + false, { "Expected object key string but found invalid escape code at character 16" } }, + { "Decode invalid unicode escape [throw error]", + json.decode, { [[ { "bad unicode \u0f6 escape" } ]] }, + false, { "Expected object key string but found invalid unicode escape code at character 17" } }, + { "Decode invalid keyword [throw error]", + json.decode, { ' [ "bad barewood", test ] ' }, + false, { "Expected value but found invalid token at character 20" } }, + { "Decode invalid number #1 [throw error]", + json.decode, { '[ -+12 ]' }, + false, { "Expected value but found invalid number at character 3" } }, + { "Decode invalid number #2 [throw error]", + json.decode, { '-v' }, + false, { "Expected value but found invalid number at character 1" } }, + { "Decode invalid number exponent [throw error]", + json.decode, { '[ 0.4eg10 ]' }, + false, { "Expected comma or array end but found invalid token at character 6" } }, + + -- Test decoding nested arrays / objects + { "Set decode_max_depth(5)", + json.decode_max_depth, { 5 }, true, { 5 } }, + { "Decode array at nested limit", + json.decode, { '[[[[[ "nested" ]]]]]' }, + true, { {{{{{ "nested" }}}}} } }, + { "Decode array over nested limit [throw error]", + json.decode, { '[[[[[[ "nested" ]]]]]]' }, + false, { "Found too many nested data structures (6) at character 6" } }, + { "Decode object at nested limit", + json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' }, + true, { {a={b={c={d={e="nested"}}}}} } }, + { "Decode object over nested limit [throw error]", + json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' }, + false, { "Found too many nested data structures (6) at character 26" } }, + { "Set decode_max_depth(1000)", + json.decode_max_depth, { 1000 }, true, { 1000 } }, + { "Decode deeply nested array [throw error]", + json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)}, + false, { "Found too many nested data structures (1001) at character 1001" } }, + + -- Test encoding nested tables + { "Set encode_max_depth(5)", + json.encode_max_depth, { 5 }, true, { 5 } }, + { "Encode nested table as array at nested limit", + json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } }, + { "Encode nested table as array after nested limit [throw error]", + json.encode, { { {{{{{"nested"}}}}} } }, + false, { "Cannot serialise, excessive nesting (6)" } }, + { "Encode nested table as object at nested limit", + json.encode, { {a={b={c={d={e="nested"}}}}} }, + true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } }, + { "Encode nested table as object over nested limit [throw error]", + json.encode, { {a={b={c={d={e={f="nested"}}}}}} }, + false, { "Cannot serialise, excessive nesting (6)" } }, + { "Encode table with cycle [throw error]", + json.encode, { testdata.table_cycle }, + false, { "Cannot serialise, excessive nesting (6)" } }, + { "Set encode_max_depth(1000)", + json.encode_max_depth, { 1000 }, true, { 1000 } }, + { "Encode deeply nested data [throw error]", + json.encode, { testdata.deeply_nested_data }, + false, { "Cannot serialise, excessive nesting (1001)" } }, + + -- Test encoding simple types + { "Encode null", + json.encode, { json.null }, true, { 'null' } }, + { "Encode true", + json.encode, { true }, true, { 'true' } }, + { "Encode false", + json.encode, { false }, true, { 'false' } }, + { "Encode empty object", + json.encode, { { } }, true, { '{}' } }, + { "Encode integer", + json.encode, { 10 }, true, { '10' } }, + { "Encode string", + json.encode, { "hello" }, true, { '"hello"' } }, + { "Encode Lua function [throw error]", + json.encode, { function () end }, + false, { "Cannot serialise function: type not supported" } }, + + -- Test decoding invalid numbers + { "Set decode_invalid_numbers(true)", + json.decode_invalid_numbers, { true }, true, { true } }, + { "Decode hexadecimal", + json.decode, { '0x6.ffp1' }, true, { 13.9921875 } }, + { "Decode numbers with leading zero", + json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } }, + { "Decode +-Inf", + json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } }, + { "Decode +-Infinity", + json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, + true, { { Inf, Inf, -Inf } } }, + { "Decode +-NaN", + json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } }, + { "Decode Infrared (not infinity) [throw error]", + json.decode, { 'Infrared' }, + false, { "Expected the end but found invalid token at character 4" } }, + { "Decode Noodle (not NaN) [throw error]", + json.decode, { 'Noodle' }, + false, { "Expected value but found invalid token at character 1" } }, + { "Set decode_invalid_numbers(false)", + json.decode_invalid_numbers, { false }, true, { false } }, + { "Decode hexadecimal [throw error]", + json.decode, { '0x6' }, + false, { "Expected value but found invalid number at character 1" } }, + { "Decode numbers with leading zero [throw error]", + json.decode, { '[ 0123, 00.33 ]' }, + false, { "Expected value but found invalid number at character 3" } }, + { "Decode +-Inf [throw error]", + json.decode, { '[ +Inf, Inf, -Inf ]' }, + false, { "Expected value but found invalid token at character 3" } }, + { "Decode +-Infinity [throw error]", + json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, + false, { "Expected value but found invalid token at character 3" } }, + { "Decode +-NaN [throw error]", + json.decode, { '[ +NaN, NaN, -NaN ]' }, + false, { "Expected value but found invalid token at character 3" } }, + { 'Set decode_invalid_numbers("on")', + json.decode_invalid_numbers, { "on" }, true, { true } }, + + -- Test encoding invalid numbers + { "Set encode_invalid_numbers(false)", + json.encode_invalid_numbers, { false }, true, { false } }, + { "Encode NaN [throw error]", + json.encode, { NaN }, + false, { "Cannot serialise number: must not be NaN or Infinity" } }, + { "Encode Infinity [throw error]", + json.encode, { Inf }, + false, { "Cannot serialise number: must not be NaN or Infinity" } }, + { "Set encode_invalid_numbers(\"null\")", + json.encode_invalid_numbers, { "null" }, true, { "null" } }, + { "Encode NaN as null", + json.encode, { NaN }, true, { "null" } }, + { "Encode Infinity as null", + json.encode, { Inf }, true, { "null" } }, + { "Set encode_invalid_numbers(true)", + json.encode_invalid_numbers, { true }, true, { true } }, + { "Encode NaN", + json.encode, { NaN }, true, { "NaN" } }, + { "Encode +Infinity", + json.encode, { Inf }, true, { "Infinity" } }, + { "Encode -Infinity", + json.encode, { -Inf }, true, { "-Infinity" } }, + { 'Set encode_invalid_numbers("off")', + json.encode_invalid_numbers, { "off" }, true, { false } }, + + -- Test encoding tables + { "Set encode_sparse_array(true, 2, 3)", + json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } }, + { "Encode sparse table as array #1", + json.encode, { { [3] = "sparse test" } }, + true, { '[null,null,"sparse test"]' } }, + { "Encode sparse table as array #2", + json.encode, { { [1] = "one", [4] = "sparse test" } }, + true, { '["one",null,null,"sparse test"]' } }, + { "Encode sparse array as object", + json.encode, { { [1] = "one", [5] = "sparse test" } }, + true, { '{"1":"one","5":"sparse test"}' } }, + { "Encode table with numeric string key as object", + json.encode, { { ["2"] = "numeric string key test" } }, + true, { '{"2":"numeric string key test"}' } }, + { "Set encode_sparse_array(false)", + json.encode_sparse_array, { false }, true, { false, 2, 3 } }, + { "Encode table with incompatible key [throw error]", + json.encode, { { [false] = "wrong" } }, + false, { "Cannot serialise boolean: table key must be a number or string" } }, + + -- Test escaping + { "Encode all octets (8-bit clean)", + json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } }, + { "Decode all escaped octets", + json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } }, + { "Decode single UTF-16 escape", + json.decode, { [["\uF800"]] }, true, { "\239\160\128" } }, + { "Decode all UTF-16 escapes (including surrogate combinations)", + json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } }, + { "Decode swapped surrogate pair [throw error]", + json.decode, { [["\uDC00\uD800"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode duplicate high surrogate [throw error]", + json.decode, { [["\uDB00\uDB00"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode duplicate low surrogate [throw error]", + json.decode, { [["\uDB00\uDB00"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode missing low surrogate [throw error]", + json.decode, { [["\uDB00"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode invalid low surrogate [throw error]", + json.decode, { [["\uDB00\uD"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + + -- Test locale support + -- + -- The standard Lua interpreter is ANSI C online doesn't support locales + -- by default. Force a known problematic locale to test strtod()/sprintf(). + { "Set locale to cs_CZ (comma separator)", function () + os.setlocale("cs_CZ") + json.new() + end }, + { "Encode number under comma locale", + json.encode, { 1.5 }, true, { '1.5' } }, + { "Decode number in array under comma locale", + json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } }, + { "Revert locale to POSIX", function () + os.setlocale("C") + json.new() + end }, + + -- Test encode_keep_buffer() and enable_number_precision() + { "Set encode_keep_buffer(false)", + json.encode_keep_buffer, { false }, true, { false } }, + { "Set encode_number_precision(3)", + json.encode_number_precision, { 3 }, true, { 3 } }, + { "Encode number with precision 3", + json.encode, { 1/3 }, true, { "0.333" } }, + { "Set encode_number_precision(14)", + json.encode_number_precision, { 14 }, true, { 14 } }, + { "Set encode_keep_buffer(true)", + json.encode_keep_buffer, { true }, true, { true } }, + + -- Test config API errors + -- Function is listed as '?' due to pcall + { "Set encode_number_precision(0) [throw error]", + json.encode_number_precision, { 0 }, + false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } }, + { "Set encode_number_precision(\"five\") [throw error]", + json.encode_number_precision, { "five" }, + false, { "bad argument #1 to '?' (number expected, got string)" } }, + { "Set encode_keep_buffer(nil, true) [throw error]", + json.encode_keep_buffer, { nil, true }, + false, { "bad argument #2 to '?' (found too many arguments)" } }, + { "Set encode_max_depth(\"wrong\") [throw error]", + json.encode_max_depth, { "wrong" }, + false, { "bad argument #1 to '?' (number expected, got string)" } }, + { "Set decode_max_depth(0) [throw error]", + json.decode_max_depth, { "0" }, + false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } }, + { "Set encode_invalid_numbers(-2) [throw error]", + json.encode_invalid_numbers, { -2 }, + false, { "bad argument #1 to '?' (invalid option '-2')" } }, + { "Set decode_invalid_numbers(true, false) [throw error]", + json.decode_invalid_numbers, { true, false }, + false, { "bad argument #2 to '?' (found too many arguments)" } }, + { "Set encode_sparse_array(\"not quite on\") [throw error]", + json.encode_sparse_array, { "not quite on" }, + false, { "bad argument #1 to '?' (invalid option 'not quite on')" } }, + + { "Reset Lua CJSON configuration", function () json = json.new() end }, + -- Wrap in a function to ensure the table returned by json.new() is used + { "Check encode_sparse_array()", + function (...) return json.encode_sparse_array(...) end, { }, + true, { false, 2, 10 } }, + + { "Encode (safe) simple value", + json_safe.encode, { true }, + true, { "true" } }, + { "Encode (safe) argument validation [throw error]", + json_safe.encode, { "arg1", "arg2" }, + false, { "bad argument #1 to '?' (expected 1 argument)" } }, + { "Decode (safe) error generation", + json_safe.decode, { "Oops" }, + true, { nil, "Expected value but found invalid token at character 1" } }, + { "Decode (safe) error generation after new()", + function(...) return json_safe.new().decode(...) end, { "Oops" }, + true, { nil, "Expected value but found invalid token at character 1" } }, +} + +print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION)) + +util.run_test_group(cjson_tests) + +for _, filename in ipairs(arg) do + util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename }, + true, { true }) +end + +local pass, total = util.run_test_summary() + +if pass == total then + print("==> Summary: all tests succeeded") +else + print(("==> Summary: %d/%d tests failed"):format(total - pass, total)) + os.exit(1) +end + +-- vi:ai et sw=4 ts=4: diff --git a/luaclib/cjson/tests/types.json b/luaclib/cjson/tests/types.json new file mode 100644 index 0000000..c01e7d2 --- /dev/null +++ b/luaclib/cjson/tests/types.json @@ -0,0 +1 @@ +{ "array": [ 10, true, null ] } diff --git a/luaclib/common/Makefile b/luaclib/common/Makefile new file mode 100644 index 0000000..e3b827f --- /dev/null +++ b/luaclib/common/Makefile @@ -0,0 +1,35 @@ + +.PHONY: all clean +CC = g++ +RM = rm -rf +##PLATFORM ?= linux + +TARGET = libcommon.a +all: $(TARGET) + +## source file path +SRC_PATH := ../../src + +## used headers file path +INCLUDE_PATH := ../../src ../../deps/lua ../../deps/uv/include + +## define CFLAGS +CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -fpic -std=c++0x -D LUA_COMPAT_5_2 -D LUA_COMPAT_5_1 + +ifeq (RELEASE,$(RELEASE)) +CFLAGS += -D RELEASE -O3 +endif + +## get all include path +CFLAGS += $(foreach dir, $(INCLUDE_PATH), -I$(dir)) + +buffer.o : $(SRC_PATH)/buffer.cpp + $(CC) -c $(CFLAGS) -o $@ $< + +OBJS := buffer.o + +$(TARGET): $(OBJS) + ar cr $@ $(OBJS) + +clean: + $(RM) *.o $(TARGET) diff --git a/luaclib/common/common.vcxproj b/luaclib/common/common.vcxproj new file mode 100644 index 0000000..bd88d80 --- /dev/null +++ b/luaclib/common/common.vcxproj @@ -0,0 +1,93 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371} + common + + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + + + + + + + + + + + $(ProjectDir) + $(ProjectName) + + + $(ProjectDir) + $(ProjectName) + + + + Level3 + Disabled + true + ..\..\src;..\..\deps\lua;..\..\deps\uv\include; + WIN32;_DEBUG;%(PreprocessorDefinitions) + + + true + + + + + + + + + Level3 + MaxSpeed + true + true + true + ..\..\src;..\..\deps\lua;..\..\deps\uv\include; + + + true + true + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/luaclib/common/common.vcxproj.filters b/luaclib/common/common.vcxproj.filters new file mode 100644 index 0000000..3f5f9f9 --- /dev/null +++ b/luaclib/common/common.vcxproj.filters @@ -0,0 +1,18 @@ + + + + + {a2d77115-a008-4b21-97b7-dba07a17574c} + + + + + src + + + + + src + + + \ No newline at end of file diff --git a/luaclib/lfs/.travis/._platform.sh b/luaclib/lfs/.travis/._platform.sh new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/.travis/._platform.sh differ diff --git a/luaclib/lfs/.travis/._setup_lua.sh b/luaclib/lfs/.travis/._setup_lua.sh new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/.travis/._setup_lua.sh differ diff --git a/luaclib/lfs/.travis/platform.sh b/luaclib/lfs/.travis/platform.sh new file mode 100644 index 0000000..4a3af0d --- /dev/null +++ b/luaclib/lfs/.travis/platform.sh @@ -0,0 +1,15 @@ +if [ -z "$PLATFORM" ]; then + PLATFORM=$TRAVIS_OS_NAME; +fi + +if [ "$PLATFORM" == "osx" ]; then + PLATFORM="macosx"; +fi + +if [ -z "$PLATFORM" ]; then + if [ "$(uname)" == "Linux" ]; then + PLATFORM="linux"; + else + PLATFORM="macosx"; + fi; +fi diff --git a/luaclib/lfs/.travis/setup_lua.sh b/luaclib/lfs/.travis/setup_lua.sh new file mode 100644 index 0000000..373e24d --- /dev/null +++ b/luaclib/lfs/.travis/setup_lua.sh @@ -0,0 +1,101 @@ +#! /bin/bash + +# A script for setting up environment for travis-ci testing. +# Sets up Lua and Luarocks. +# LUA must be "lua5.1", "lua5.2" or "luajit". +# luajit2.0 - master v2.0 +# luajit2.1 - master v2.1 + +LUAJIT_BASE="LuaJIT-2.0.3" + +source .travis/platform.sh + +LUAJIT="no" + +if [ "$PLATFORM" == "macosx" ]; then + if [ "$LUA" == "luajit" ]; then + LUAJIT="yes"; + fi + if [ "$LUA" == "luajit2.0" ]; then + LUAJIT="yes"; + fi + if [ "$LUA" == "luajit2.1" ]; then + LUAJIT="yes"; + fi; +elif [ "$(expr substr $LUA 1 6)" == "luajit" ]; then + LUAJIT="yes"; +fi + +if [ "$LUAJIT" == "yes" ]; then + + if [ "$LUA" == "luajit" ]; then + curl http://luajit.org/download/$LUAJIT_BASE.tar.gz | tar xz; + else + git clone http://luajit.org/git/luajit-2.0.git $LUAJIT_BASE; + fi + + cd $LUAJIT_BASE + + if [ "$LUA" == "luajit2.1" ]; then + git checkout v2.1; + fi + + make && sudo make install + + if [ "$LUA" == "luajit2.1" ]; then + sudo ln -s /usr/local/bin/luajit-2.1.0-alpha /usr/local/bin/luajit + sudo ln -s /usr/local/bin/luajit /usr/local/bin/lua; + else + sudo ln -s /usr/local/bin/luajit /usr/local/bin/lua; + fi; + +else + if [ "$LUA" == "lua5.1" ]; then + curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz + cd lua-5.1.5; + elif [ "$LUA" == "lua5.2" ]; then + curl http://www.lua.org/ftp/lua-5.2.3.tar.gz | tar xz + cd lua-5.2.3; + elif [ "$LUA" == "lua5.3" ]; then + curl http://www.lua.org/work/lua-5.3.0-beta.tar.gz | tar xz + cd lua-5.3.0-beta; + fi + sudo make $PLATFORM install; +fi + +cd $TRAVIS_BUILD_DIR; + +LUAROCKS_BASE=luarocks-$LUAROCKS + +# curl http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz | tar xz + +git clone https://github.com/keplerproject/luarocks.git $LUAROCKS_BASE +cd $LUAROCKS_BASE + +git checkout v$LUAROCKS + +if [ "$LUA" == "luajit" ]; then + ./configure --lua-suffix=jit --with-lua-include=/usr/local/include/luajit-2.0; +elif [ "$LUA" == "luajit2.0" ]; then + ./configure --lua-suffix=jit --with-lua-include=/usr/local/include/luajit-2.0; +elif [ "$LUA" == "luajit2.1" ]; then + ./configure --lua-suffix=jit --with-lua-include=/usr/local/include/luajit-2.1; +else + ./configure; +fi + +make build && sudo make install + +cd $TRAVIS_BUILD_DIR + +rm -rf $LUAROCKS_BASE + +if [ "$LUAJIT" == "yes" ]; then + rm -rf $LUAJIT_BASE; +elif [ "$LUA" == "lua5.1" ]; then + rm -rf lua-5.1.5; +elif [ "$LUA" == "lua5.2" ]; then + rm -rf lua-5.2.3; +elif [ "$LUA" == "lua5.3" ]; then + rm -rf lua-5.3.0-beta; +fi diff --git a/luaclib/lfs/LICENSE b/luaclib/lfs/LICENSE new file mode 100644 index 0000000..8475345 --- /dev/null +++ b/luaclib/lfs/LICENSE @@ -0,0 +1,21 @@ +Copyright © 2003-2014 Kepler Project. + +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/lfs/Makefile b/luaclib/lfs/Makefile new file mode 100644 index 0000000..4005c75 --- /dev/null +++ b/luaclib/lfs/Makefile @@ -0,0 +1,61 @@ +.PHONY: all clean +CC = gcc +RM = rm -rf +##PLATFORM ?= linux + +TARGET = lfs.so +all: $(TARGET) + +## source file path +SRC_PATH := . + +## used headers file path +INCLUDE_PATH := ../../deps/lua + +## used include librarys file path +LIBRARY_PATH := ../../deps/lua + +## need libs, add at here +LIBS := nlua + +## define CFLAGS +CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -fpic -D LUA_COMPAT_5_2 -D LUA_COMPAT_5_1 +ifeq (RELEASE,$(RELEASE)) +CFLAGS += -D RELEASE -O3 +endif + +## define LDFLAGS +LDFLAGS += -shared + +ifeq (darwin,$(PLATFORM)) +LDFLAGS += -undefined dynamic_lookup \ + -framework Foundation \ + -framework CoreServices \ + -framework ApplicationServices +endif + +ifeq (linux,$(PLATFORM)) +LIBS += dl +endif + + +## get all source files +SRCS += $(wildcard $(SRC_PATH)/*.c) + +## all .o based on all .c +OBJS := $(SRCS:.c=.o) + +## get all include path +CFLAGS += $(foreach dir, $(INCLUDE_PATH), -I$(dir)) +.c.o: + $(CC) -c $(CFLAGS) -o $@ $< + +## get all library path +LDFLAGS += $(foreach lib, $(LIBRARY_PATH), -L$(lib)) +## get all librarys +LDFLAGS += $(foreach lib, $(LIBS), -l$(lib)) +$(TARGET): $(OBJS) + $(CC) -o $@ $(OBJS) $(LDFLAGS) + +clean: + $(RM) *.o $(TARGET) diff --git a/luaclib/lfs/Makefile.bak b/luaclib/lfs/Makefile.bak new file mode 100644 index 0000000..b834a4d --- /dev/null +++ b/luaclib/lfs/Makefile.bak @@ -0,0 +1,25 @@ +# $Id: Makefile,v 1.36 2009/09/21 17:02:44 mascarenhas Exp $ + +T= lfs + +CONFIG= ./config + +include $(CONFIG) + +SRCS= src/$T.c +OBJS= src/$T.o + +lib: src/lfs.so + +src/lfs.so: $(OBJS) + MACOSX_DEPLOYMENT_TARGET="10.3"; export MACOSX_DEPLOYMENT_TARGET; $(CC) $(CFLAGS) $(LIB_OPTION) -o src/lfs.so $(OBJS) + +test: lib + LUA_CPATH=./src/?.so lua tests/test.lua + +install: + mkdir -p $(LUA_LIBDIR) + cp src/lfs.so $(LUA_LIBDIR) + +clean: + rm -f src/lfs.so $(OBJS) diff --git a/luaclib/lfs/Makefile.win b/luaclib/lfs/Makefile.win new file mode 100644 index 0000000..65cab81 --- /dev/null +++ b/luaclib/lfs/Makefile.win @@ -0,0 +1,25 @@ +# $Id: Makefile.win,v 1.11 2008/05/07 19:06:37 carregal Exp $ + +T= lfs + +include config.win + +SRCS= src\$T.c +OBJS= src\$T.obj + +lib: src\lfs.dll + +.c.obj: + $(CC) /c /Fo$@ $(CFLAGS) $< + +src\lfs.dll: $(OBJS) + link /dll /def:src\$T.def /out:src\lfs.dll $(OBJS) "$(LUA_LIB)" + IF EXIST src\lfs.dll.manifest mt -manifest src\lfs.dll.manifest -outputresource:src\lfs.dll;2 + +install: src\lfs.dll + IF NOT EXIST "$(LUA_LIBDIR)" mkdir "$(LUA_LIBDIR)" + copy src\lfs.dll "$(LUA_LIBDIR)" + +clean: + del src\lfs.dll $(OBJS) src\$T.lib src\$T.exp + IF EXIST src\lfs.dll.manifest del src\lfs.dll.manifest \ No newline at end of file diff --git a/luaclib/lfs/README b/luaclib/lfs/README new file mode 100644 index 0000000..005c38e --- /dev/null +++ b/luaclib/lfs/README @@ -0,0 +1,82 @@ +LuaFileSystem - File System Library for Lua +Copyright 2003 Kepler Project +http://www.keplerproject.org/luafilesystem + +Description +----------- +LuaFileSystem is a Lua library developed to complement the set of functions +related to file systems offered by the standard Lua distribution. + +LuaFileSystem offers a portable way to access the underlying directory structure and file attributes. +LuaFileSystem is free software and uses the same license as Lua 5.1 + +LuaRocks Installation +--------------------- +luarocks install luafilesystem + + +Documentation +------------- +Please check the documentation at doc/us/ for more information. + +History +------- + +Version 1.6.3 [15/Jan/2012] + * Lua 5.3 compatibility + * Assorted bugfixes + +Version 1.6.2 [??/Oct/2012] + * Full Lua 5.2 compatibility (with Lua 5.1 fallbacks) + +Version 1.6.1 [01/Oct/2012] + * fix build for Lua 5.2 + +Version 1.6.0 [26/Sep/2012] + * getcwd fix for Android + * support for Lua 5.2 + * add lfs.link + * other bug fixes + +Version 1.5.0 [20/Oct/2009] + * added explicit next and close methods to second return value of lfs.dir (the directory object), for explicit iteration or explicit closing. + * added directory locking via lfs.lock_dir function (see the manual). + +Version 1.4.2 [03/Feb/2009] + + * fixed bug [#13198] lfs.attributes(filename, 'size') overflow on files > 2 Gb again (bug report and patch by KUBO Takehiro). + * fixed bug [#39794] Compile error on Solaris 10 (bug report and patch by Aaron B). + * fixed compilation problems with Borland C. + +Version 1.4.1 [07/May/2008] + + * documentation review + * fixed Windows compilation issues + * fixed bug in the Windows tests (patch by Shmuel Zeigerman) + * fixed bug [#2185] lfs.attributes(filename, 'size') overflow on files > 2 Gb + +Version 1.4.0 [13/Feb/2008] + + * added function lfs.setmode (works only in Windows systems). + * lfs.attributes raises an error if attribute does not exist + +Version 1.3.0 [26/Oct/2007] + + * added function lfs.symlinkattributes (works only in non Windows systems). + +Version 1.2.1 [08/May/2007] + + * compatible only with Lua 5.1 (Lua 5.0 support was dropped) + +Version 1.2 [15/Mar/2006] + + * added optional argument to lfs.attributes + * added function lfs.rmdir + * bug correction on lfs.dir + +Version 1.1 [30/May/2005] + + * added function lfs.touch. + +Version 1.0 [21/Jan/2005] +Version 1.0 Beta [10/Nov/2004] diff --git a/luaclib/lfs/config b/luaclib/lfs/config new file mode 100644 index 0000000..cfd4c6a --- /dev/null +++ b/luaclib/lfs/config @@ -0,0 +1,24 @@ +# Installation directories + +# Default installation prefix +PREFIX=/usr/local + +# System's libraries directory (where binary libraries are installed) +LUA_LIBDIR= $(PREFIX)/lib/lua/5.1 + +# Lua includes directory +LUA_INC= $(PREFIX)/include + +# OS dependent +LIB_OPTION= -shared #for Linux +#LIB_OPTION= -bundle -undefined dynamic_lookup #for MacOS X + +LIBNAME= $T.so.$V + +# Compilation directives +WARN= -O2 -Wall -fPIC -W -Waggregate-return -Wcast-align -Wmissing-prototypes -Wnested-externs -Wshadow -Wwrite-strings -pedantic +INCS= -I$(LUA_INC) +CFLAGS= $(WARN) $(INCS) +CC= gcc + +# $Id: config,v 1.21 2007/10/27 22:42:32 carregal Exp $ diff --git a/luaclib/lfs/config.win b/luaclib/lfs/config.win new file mode 100644 index 0000000..50e81f6 --- /dev/null +++ b/luaclib/lfs/config.win @@ -0,0 +1,19 @@ +# Installation directories +# System's libraries directory (where binary libraries are installed) +LUA_LIBDIR= "c:\lua5.1" + +# Lua includes directory +LUA_INC= "c:\lua5.1\include" + +# Lua library +LUA_LIB= "c:\lua5.1\lua5.1.lib" + +LIBNAME= $T.dll + +# Compilation directives +WARN= /O2 +INCS= /I$(LUA_INC) +CFLAGS= /MD $(WARN) $(INCS) +CC= cl + +# $Id: config.win,v 1.7 2008/03/25 17:39:29 mascarenhas Exp $ diff --git a/luaclib/lfs/doc/._us b/luaclib/lfs/doc/._us new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/doc/._us differ diff --git a/luaclib/lfs/doc/us/._doc.css b/luaclib/lfs/doc/us/._doc.css new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/doc/us/._doc.css differ diff --git a/luaclib/lfs/doc/us/._examples.html b/luaclib/lfs/doc/us/._examples.html new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/doc/us/._examples.html differ diff --git a/luaclib/lfs/doc/us/._index.html b/luaclib/lfs/doc/us/._index.html new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/doc/us/._index.html differ diff --git a/luaclib/lfs/doc/us/._license.html b/luaclib/lfs/doc/us/._license.html new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/doc/us/._license.html differ diff --git a/luaclib/lfs/doc/us/._luafilesystem.png b/luaclib/lfs/doc/us/._luafilesystem.png new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/doc/us/._luafilesystem.png differ diff --git a/luaclib/lfs/doc/us/._manual.html b/luaclib/lfs/doc/us/._manual.html new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/doc/us/._manual.html differ diff --git a/luaclib/lfs/doc/us/doc.css b/luaclib/lfs/doc/us/doc.css new file mode 100644 index 0000000..e816a7e --- /dev/null +++ b/luaclib/lfs/doc/us/doc.css @@ -0,0 +1,212 @@ +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color:#ffffff; margin:0px; +} + +code { + font-family: "Andale Mono", monospace; +} + +tt { + font-family: "Andale Mono", monospace; +} + +body, td, th { font-size: 11pt; } + +h1, h2, h3, h4 { margin-left: 0em; } + +textarea, pre, tt { font-size:10pt; } +body, td, th { color:#000000; } +small { font-size:0.85em; } +h1 { font-size:1.5em; } +h2 { font-size:1.25em; } +h3 { font-size:1.15em; } +h4 { font-size:1.06em; } + +a:link { font-weight:bold; color: #004080; text-decoration: none; } +a:visited { font-weight:bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration:underline; } +hr { color:#cccccc } +img { border-width: 0px; } + +h3 { padding-top: 1em; } + +p { margin-left: 1em; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; + margin-left: 0em; +} + +blockquote { margin-left: 3em; } + +.example { + background-color: rgb(245, 245, 245); + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-style: solid; + border-right-style: solid; + border-bottom-style: solid; + border-left-style: solid; + border-top-color: silver; + border-right-color: silver; + border-bottom-color: silver; + border-left-color: silver; + padding: 1em; + margin-left: 1em; + margin-right: 1em; + font-family: "Andale Mono", monospace; + font-size: smaller; +} + +hr { + margin-left: 0em; + background: #00007f; + border: 0px; + height: 1px; +} + +ul { list-style-type: disc; } + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } +table.index ul { padding-top: 0em; margin-top: 0em; } + +table { + border: 1px solid black; + border-collapse: collapse; + margin-left: auto; + margin-right: auto; +} + +th { + border: 1px solid black; + padding: 0.5em; +} + +td { + border: 1px solid black; + padding: 0.5em; +} +div.header, div.footer { margin-left: 0em; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#product_logo { +} + +#product_name { +} + +#product_description { +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 12em; + margin: 0; + vertical-align: top; + background-color: #f0f0f0; + overflow:visible; +} + +#navigation h1 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align:left; + margin:0px; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul { + font-size:1em; + list-style-type: none; + padding: 0; + margin: 1px; +} + +#navigation li { + text-indent: -1em; + margin: 0em 0em 0em 0.5em; + display: block; + padding: 3px 0px 0px 12px; +} + +#navigation li li a { + padding: 0px 3px 0px -1em; +} + +#content { + margin-left: 12em; + padding: 1em; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + margin: 0; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 10pt "Times New Roman", "TimeNR", Times, serif; + } + a { + font-weight:bold; color: #004080; text-decoration: underline; + } + #main { + background-color: #ffffff; border-left: 0px; + } + #container { + margin-left: 2%; margin-right: 2%; background-color: #ffffff; + } + #content { + margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; + } + #navigation { + display: none; + } + #product_logo { + display: none; + } + #about img { + display: none; + } + .example { + font-family: "Andale Mono", monospace; + font-size: 8pt; + page-break-inside: avoid; + } +} diff --git a/luaclib/lfs/doc/us/examples.html b/luaclib/lfs/doc/us/examples.html new file mode 100644 index 0000000..2c1644c --- /dev/null +++ b/luaclib/lfs/doc/us/examples.html @@ -0,0 +1,103 @@ + + + + LuaFileSystem + + + + + + +

+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

Examples

+ +

Directory iterator

+ +

The following example iterates over a directory and recursively lists the +attributes for each file inside it.

+ +
+local lfs = require"lfs"
+
+function attrdir (path)
+    for file in lfs.dir(path) do
+        if file ~= "." and file ~= ".." then
+            local f = path..'/'..file
+            print ("\t "..f)
+            local attr = lfs.attributes (f)
+            assert (type(attr) == "table")
+            if attr.mode == "directory" then
+                attrdir (f)
+            else
+                for name, value in pairs(attr) do
+                    print (name, value)
+                end
+            end
+        end
+    end
+end
+
+attrdir (".")
+
+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: examples.html,v 1.8 2007/12/14 15:28:04 carregal Exp $

+
+ +
+ + + diff --git a/luaclib/lfs/doc/us/index.html b/luaclib/lfs/doc/us/index.html new file mode 100644 index 0000000..2bb7f5d --- /dev/null +++ b/luaclib/lfs/doc/us/index.html @@ -0,0 +1,218 @@ + + + + LuaFileSystem + + + + + + +
+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

Overview

+ +

LuaFileSystem is a Lua library +developed to complement the set of functions related to file +systems offered by the standard Lua distribution.

+ +

LuaFileSystem offers a portable way to access +the underlying directory structure and file attributes.

+ +

LuaFileSystem is free software and uses the same +license as Lua 5.1.

+ +

Status

+ +

Current version is 1.6.3. It works with Lua 5.1, 5.2 and 5.3.

+ +

Download

+ +

LuaFileSystem source can be downloaded from its +Github +page.

+ +

History

+ +
+
Version 1.6.3 [15/Jan/2015]
+
    +
  • Lua 5.3 support.
  • +
  • Assorted bugfixes.
  • +
+ +
Version 1.6.2 [??/Oct/2012]
+
    +
  • Full Lua 5.2 compatibility (with Lua 5.1 fallbacks)
  • +
+ +
Version 1.6.1 [01/Oct/2012]
+
    +
  • fix build for Lua 5.2
  • +
+ +
Version 1.6.0 [26/Sep/2012]
+
    +
  • getcwd fix for Android
  • +
  • support for Lua 5.2
  • +
  • add lfs.link
  • +
  • other bug fixes
  • +
+ +
Version 1.5.0 [20/Oct/2009]
+
    +
  • Added explicit next and close methods to second return value of lfs.dir +(the directory object), for explicit iteration or explicit closing.
  • +
  • Added directory locking via lfs.lock_dir function (see the manual).
  • +
+
Version 1.4.2 [03/Feb/2009]
+
+
    +
  • fixed bug [#13198] + lfs.attributes(filename, 'size') overflow on files > 2 Gb again (bug report and patch by KUBO Takehiro).
  • +
  • fixed bug [#39794] + Compile error on Solaris 10 (bug report and patch by Aaron B).
  • +
  • fixed compilation problems with Borland C.
  • +
+
+ +
Version 1.4.1 [07/May/2008]
+
+
    +
  • documentation review
  • +
  • fixed Windows compilation issues
  • +
  • fixed bug in the Windows tests (patch by Shmuel Zeigerman)
  • +
  • fixed bug [#2185] + lfs.attributes(filename, 'size') overflow on files > 2 Gb +
  • +
+
+ +
Version 1.4.0 [13/Feb/2008]
+
+
    +
  • added function + lfs.setmode + (works only in Windows systems).
  • +
  • lfs.attributes + raises an error if attribute does not exist
  • +
+
+ +
Version 1.3.0 [26/Oct/2007]
+
+ +
+ +
Version 1.2.1 [08/May/2007]
+
+
    +
  • compatible only with Lua 5.1 (Lua 5.0 support was dropped)
  • +
+
+ +
Version 1.2 [15/Mar/2006]
+
+ +
+ +
Version 1.1 [30/May/2005]
+
+ +
+ +
Version 1.0 [21/Jan/2005]
+
+ +
Version 1.0 Beta [10/Nov/2004]
+
+
+ +

Credits

+ +

LuaFileSystem was designed by Roberto Ierusalimschy, +André Carregal and Tomás Guisasola as part of the +Kepler Project, +which holds its copyright. LuaFileSystem is currently maintained by Fábio Mascarenhas.

+ +

Contact us

+ +

For more information please +contact us. +Comments are welcome!

+ +

You can also reach other Kepler developers and users on the Kepler Project +mailing list.

+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: index.html,v 1.44 2009/02/04 21:21:33 carregal Exp $

+
+ +
+ + + diff --git a/luaclib/lfs/doc/us/license.html b/luaclib/lfs/doc/us/license.html new file mode 100644 index 0000000..3003381 --- /dev/null +++ b/luaclib/lfs/doc/us/license.html @@ -0,0 +1,122 @@ + + + + LuaFileSystem + + + + + + +
+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

License

+ +

+LuaFileSystem is free software: it can be used for both academic +and commercial purposes at absolutely no cost. There are no +royalties or GNU-like "copyleft" restrictions. LuaFileSystem +qualifies as +Open Source +software. +Its licenses are compatible with +GPL. +LuaFileSystem is not in the public domain and the +Kepler Project +keep its copyright. +The legal details are below. +

+ +

The spirit of the license is that you are free to use +LuaFileSystem for any purpose at no cost without having to ask us. +The only requirement is that if you do use LuaFileSystem, then you +should give us credit by including the appropriate copyright notice +somewhere in your product or its documentation.

+ +

The LuaFileSystem library is designed and implemented by Roberto +Ierusalimschy, André Carregal and Tomás Guisasola. +The implementation is not derived from licensed software.

+ +
+

Copyright © 2003 Kepler Project.

+ +

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.

+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: license.html,v 1.13 2008/02/11 22:42:21 carregal Exp $

+
+ +
+ + + diff --git a/luaclib/lfs/doc/us/luafilesystem.png b/luaclib/lfs/doc/us/luafilesystem.png new file mode 100644 index 0000000..e1dd8c6 Binary files /dev/null and b/luaclib/lfs/doc/us/luafilesystem.png differ diff --git a/luaclib/lfs/doc/us/manual.html b/luaclib/lfs/doc/us/manual.html new file mode 100644 index 0000000..33c1cbe --- /dev/null +++ b/luaclib/lfs/doc/us/manual.html @@ -0,0 +1,280 @@ + + + + LuaFileSystem + + + + + + +
+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

Introduction

+ +

LuaFileSystem is a Lua library +developed to complement the set of functions related to file +systems offered by the standard Lua distribution.

+ +

LuaFileSystem offers a portable way to access +the underlying directory structure and file attributes.

+ +

Building

+ +

+LuaFileSystem should be built with Lua 5.1 so the language library +and header files for the target version must be installed properly. +

+ +

+LuaFileSystem offers a Makefile and a separate configuration file, +config, +which should be edited to suit your installation before running +make. +The file has some definitions like paths to the external libraries, +compiler options and the like. +

+ +

On Windows, the C runtime used to compile LuaFileSystem must be the same +runtime that Lua uses, or some LuaFileSystem functions will not work.

+ +

Installation

+ +

The easiest way to install LuaFileSystem is to use LuaRocks:

+ +
+luarocks install luafilesystem
+
+ +

If you prefer to install LuaFileSystem manually, the compiled binary should be copied to a directory in your +C path.

+ +

Reference

+ +

+LuaFileSystem offers the following functions: +

+ +
+
lfs.attributes (filepath [, aname])
+
Returns a table with the file attributes corresponding to + filepath (or nil followed by an error message + in case of error). + If the second optional argument is given, then only the value of the + named attribute is returned (this use is equivalent to + lfs.attributes(filepath).aname, but the table is not created + and only one attribute is retrieved from the O.S.). + The attributes are described as follows; + attribute mode is a string, all the others are numbers, + and the time related attributes use the same time reference of + os.time: +
+
dev
+
on Unix systems, this represents the device that the inode resides on. On Windows systems, + represents the drive number of the disk containing the file
+ +
ino
+
on Unix systems, this represents the inode number. On Windows systems this has no meaning
+ +
mode
+
string representing the associated protection mode (the values could be + file, directory, link, socket, + named pipe, char device, block device or + other)
+ +
nlink
+
number of hard links to the file
+ +
uid
+
user-id of owner (Unix only, always 0 on Windows)
+ +
gid
+
group-id of owner (Unix only, always 0 on Windows)
+ +
rdev
+
on Unix systems, represents the device type, for special file inodes. + On Windows systems represents the same as dev
+ +
access
+
time of last access
+ +
modification
+
time of last data modification
+ +
change
+
time of last file status change
+ +
size
+
file size, in bytes
+ +
blocks
+
block allocated for file; (Unix only)
+ +
blksize
+
optimal file system I/O blocksize; (Unix only)
+
+ This function uses stat internally thus if the given + filepath is a symbolic link, it is followed (if it points to + another link the chain is followed recursively) and the information + is about the file it refers to. + To obtain information about the link itself, see function + lfs.symlinkattributes. +
+ +
lfs.chdir (path)
+
Changes the current working directory to the given + path.
+ Returns true in case of success or nil plus an + error string.
+ +
lfs.lock_dir(path, [seconds_stale])
+
Creates a lockfile (called lockfile.lfs) in path if it does not + exist and returns the lock. If the lock already exists checks if + it's stale, using the second parameter (default for the second + parameter is INT_MAX, which in practice means the lock will never + be stale. To free the the lock call lock:free().
+ In case of any errors it returns nil and the error message. In + particular, if the lock exists and is not stale it returns the + "File exists" message.
+ +
lfs.currentdir ()
+
Returns a string with the current working directory or nil + plus an error string.
+ +
iter, dir_obj = lfs.dir (path)
+
+ Lua iterator over the entries of a given directory. + Each time the iterator is called with dir_obj it returns a directory entry's name as a string, or + nil if there are no more entries. You can also iterate by calling dir_obj:next(), and + explicitly close the directory before the iteration finished with dir_obj:close(). + Raises an error if path is not a directory. +
+ +
lfs.lock (filehandle, mode[, start[, length]])
+
Locks a file or a part of it. This function works on open files; the + file handle should be specified as the first argument. + The string mode could be either + r (for a read/shared lock) or w (for a + write/exclusive lock). The optional arguments start + and length can be used to specify a starting point and + its length; both should be numbers.
+ Returns true if the operation was successful; in + case of error, it returns nil plus an error string. +
+ +
lfs.link (old, new[, symlink])
+
Creates a link. The first argument is the object to link to + and the second is the name of the link. If the optional third + argument is true, the link will by a symbolic link (by default, a + hard link is created). +
+ +
lfs.mkdir (dirname)
+
Creates a new directory. The argument is the name of the new + directory.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string. +
+ +
lfs.rmdir (dirname)
+
Removes an existing directory. The argument is the name of the directory.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string.
+ +
lfs.setmode (file, mode)
+
Sets the writing mode for a file. The mode string can be either "binary" or "text". + Returns true followed the previous mode string for the file, or + nil followed by an error string in case of errors. + On non-Windows platforms, where the two modes are identical, + setting the mode has no effect, and the mode is always returned as binary. +
+ +
lfs.symlinkattributes (filepath [, aname])
+
Identical to lfs.attributes except that + it obtains information about the link itself (not the file it refers to). + On Windows this function does not yet support links, and is identical to + lfs.attributes. +
+ +
lfs.touch (filepath [, atime [, mtime]])
+
Set access and modification times of a file. This function is + a bind to utime function. The first argument is the + filename, the second argument (atime) is the access time, + and the third argument (mtime) is the modification time. + Both times are provided in seconds (which should be generated with + Lua standard function os.time). + If the modification time is omitted, the access time provided is used; + if both times are omitted, the current time is used.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string. +
+ +
lfs.unlock (filehandle[, start[, length]])
+
Unlocks a file or a part of it. This function works on + open files; the file handle should be specified as the first + argument. The optional arguments start and + length can be used to specify a starting point and its + length; both should be numbers.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string. +
+
+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: manual.html,v 1.45 2009/06/03 20:53:55 mascarenhas Exp $

+
+ +
+ + + diff --git a/luaclib/lfs/lfs.c b/luaclib/lfs/lfs.c new file mode 100644 index 0000000..4cb5875 --- /dev/null +++ b/luaclib/lfs/lfs.c @@ -0,0 +1,898 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem) +** +** File system manipulation library. +** This library offers these functions: +** lfs.attributes (filepath [, attributename]) +** lfs.chdir (path) +** lfs.currentdir () +** lfs.dir (path) +** lfs.lock (fh, mode) +** lfs.lock_dir (path) +** lfs.mkdir (path) +** lfs.rmdir (path) +** lfs.setmode (filepath, mode) +** lfs.symlinkattributes (filepath [, attributename]) -- thanks to Sam Roberts +** lfs.touch (filepath [, atime [, mtime]]) +** lfs.unlock (fh) +** +** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $ +*/ + +#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 +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include "lfs.h" + +#define LFS_VERSION "1.6.3" +#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 luaL_newlib(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 'getcwd' for systems that do not implement it */ +#ifdef NO_GETCWD +#define getcwd(p,s) NULL +#define getcwd_error "Function 'getcwd' not provided by system" +#else +#define getcwd_error strerror(errno) + #ifdef _WIN32 + /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ + #define LFS_MAXPATHLEN MAX_PATH + #else + /* For MAXPATHLEN: */ + #include + #define LFS_MAXPATHLEN MAXPATHLEN + #endif +#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(L,file,m) ((void)L, setmode(_fileno(file), m)) + #define STAT_STRUCT struct stati64 + #else + #define lfs_setmode(L,file,m) ((void)L, _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(L,file,m) ((void)L, (void)file, (void)m, 0) +#define STAT_STRUCT struct stat +#define STAT_FUNC stat +#define LSTAT_FUNC lstat +#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 i, const char *info) +{ + if (i==-1) + return pusherror(L, info); + lua_pushinteger(L, i); + 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) { + char *path; + /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ + char buf[LFS_MAXPATHLEN]; + if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) { + lua_pushnil(L); + lua_pushstring(L, getcwd_error); + return 2; + } + else { + lua_pushstring(L, path); + return 1; + } +} + +/* +** 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) { + FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); + if (fh == NULL) { + luaL_error (L, "%s: not a file", funcname); + return 0; + } else if (*fh == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return *fh; +} + + +/* +** +*/ +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 = 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 = 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(L, 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]); + goto exit; + } + } + lua_pushnil(L); + exit: + return 2; + } else { + int en = errno; + lua_pushnil(L); + lua_pushfstring(L, "%s", strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + +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); + return pushresult(L, + (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL); +#else + return pusherror(L, "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); + int fail; +#ifdef _WIN32 + fail = _mkdir (path); +#else + fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | + S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH ); +#endif + if (fail) { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + + +/* +** Removes a directory. +** @param #1 Directory path. +*/ +static int remove_dir (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + int fail; + + fail = rmdir (path); + + if (fail) { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + + +/* +** 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 file +*/ +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; + } + if (utime (file, buf)) { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror (errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + + +/* 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'", file); + return 2; + } + 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"); + } + /* creates a table if none is given */ + 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); +} + + +/* +** Get symbolic link information using lstat. +*/ +static int link_info (lua_State *L) { + return _file_info_ (L, LSTAT_FUNC); +} + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + 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_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "LuaFileSystem "LFS_VERSION); + lua_settable (L, -3); +} + + +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}, +}; + +int luaopen_lfs (lua_State *L) { + dir_create_meta (L); + lock_create_meta (L); + luaL_newlib (L, fslib); + lua_pushvalue(L, -1); + lua_setglobal(L, LFS_LIBNAME); + set_info (L); + return 1; +} diff --git a/luaclib/lfs/lfs.def b/luaclib/lfs/lfs.def new file mode 100644 index 0000000..4298a15 --- /dev/null +++ b/luaclib/lfs/lfs.def @@ -0,0 +1,3 @@ +LIBRARY + EXPORTS + luaopen_lfs \ No newline at end of file diff --git a/luaclib/lfs/lfs.h b/luaclib/lfs/lfs.h new file mode 100644 index 0000000..ddd454d --- /dev/null +++ b/luaclib/lfs/lfs.h @@ -0,0 +1,25 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem) +** +** $Id: lfs.h,v 1.5 2008/02/19 20:08:23 mascarenhas Exp $ +*/ + +/* 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 fileno(f) (_fileno(f)) +#endif + + +int luaopen_lfs (lua_State *L); diff --git a/luaclib/lfs/lfs.vcxproj b/luaclib/lfs/lfs.vcxproj new file mode 100644 index 0000000..af5c6d3 --- /dev/null +++ b/luaclib/lfs/lfs.vcxproj @@ -0,0 +1,85 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E} + lfs + + + + DynamicLibrary + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(ProjectDir) + + + + Level3 + Disabled + true + ..\..\deps\lua; + WIN32;_DEBUG;_WINDOWS;_USRDLL;LUA_CORE;LUA_BUILD_AS_DLL;LUA_COMPAT_5_2;LUA_COMPAT_5_1;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + ..\..\deps\lua; + nlua.lib + lfs.def + + + copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir) + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/luaclib/lfs/lfs.vcxproj.filters b/luaclib/lfs/lfs.vcxproj.filters new file mode 100644 index 0000000..6e793af --- /dev/null +++ b/luaclib/lfs/lfs.vcxproj.filters @@ -0,0 +1,18 @@ + + + + + {395fddde-2cd4-441d-9334-8428e206d9cb} + + + + + src + + + + + src + + + \ No newline at end of file diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.3.0-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.3.0-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.3.0-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.4.0-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.4.0-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.4.0-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.4.0-2.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.4.0-2.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.4.0-2.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.4.1-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.4.1-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.4.1-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.4.1rc1-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.4.1rc1-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.4.1rc1-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.4.2-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.4.2-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.4.2-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.5.0-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.5.0-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.5.0-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.6.0-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.6.0-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.6.0-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.6.1-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.6.1-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.6.1-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.6.2-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.6.2-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.6.2-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-1.6.3-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-1.6.3-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-1.6.3-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-cvs-1.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-cvs-1.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-cvs-1.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-cvs-2.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-cvs-2.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-cvs-2.rockspec differ diff --git a/luaclib/lfs/rockspecs/._luafilesystem-cvs-3.rockspec b/luaclib/lfs/rockspecs/._luafilesystem-cvs-3.rockspec new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/rockspecs/._luafilesystem-cvs-3.rockspec differ diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.3.0-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.3.0-1.rockspec new file mode 100644 index 0000000..d4d484f --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.3.0-1.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" +version = "1.3.0-1" +source = { + url = "http://luaforge.net/frs/download.php/2679/luafilesystem-1.3.0.tar.gz" +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "make", + build_variables = { + LUA_INC = "$(LUA_INCDIR)", + LIB_OPTION = "$(LIBFLAG)" + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)" + } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.4.0-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.4.0-1.rockspec new file mode 100644 index 0000000..b693618 --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.4.0-1.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" +version = "1.4.0-1" +source = { + url = "http://luaforge.net/frs/download.php/3158/luafilesystem-1.4.0.tar.gz" +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "make", + build_variables = { + LUA_INC = "$(LUA_INCDIR)", + LIB_OPTION = "$(LIBFLAG)" + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)" + } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.4.0-2.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.4.0-2.rockspec new file mode 100644 index 0000000..f7ed871 --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.4.0-2.rockspec @@ -0,0 +1,43 @@ +package = "LuaFileSystem" +version = "1.4.0-2" +source = { + url = "http://luaforge.net/frs/download.php/3158/luafilesystem-1.4.0.tar.gz" +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} +dependencies = { + "lua >= 5.1" +} +build = { + platforms = { + unix = { + type = "make", + build_variables = { + LIB_OPTION = "$(LIBFLAG)", + CFLAGS = "$(CFLAGS) -I$(LUA_INCDIR)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)" + } + }, + win32 = { + type = "make", + build_variables = { + LUA_LIB = "$(LUA_LIBDIR)\\lua5.1.lib", + CFLAGS = "/MD $(CFLAGS) /I$(LUA_INCDIR)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)", + LUA_DIR = "$(LUADIR)", + BIN_DIR = "$(BINDIR)" + } + } + } +} \ No newline at end of file diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.4.1-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.4.1-1.rockspec new file mode 100644 index 0000000..db3a3eb --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.4.1-1.rockspec @@ -0,0 +1,43 @@ +package = "LuaFileSystem" +version = "1.4.1-1" +source = { + url = "http://luaforge.net/frs/download.php/3345/luafilesystem-1.4.1.tar.gz", +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} +dependencies = { + "lua >= 5.1" +} +build = { + platforms = { + unix = { + type = "make", + build_variables = { + LIB_OPTION = "$(LIBFLAG)", + CFLAGS = "$(CFLAGS) -I$(LUA_INCDIR) $(STAT64)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)" + } + }, + win32 = { + type = "make", + build_variables = { + LUA_LIB = "$(LUA_LIBDIR)\\lua5.1.lib", + CFLAGS = "/MD $(CFLAGS) /I$(LUA_INCDIR)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)", + LUA_DIR = "$(LUADIR)", + BIN_DIR = "$(BINDIR)" + } + } + } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.4.1rc1-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.4.1rc1-1.rockspec new file mode 100644 index 0000000..1194711 --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.4.1rc1-1.rockspec @@ -0,0 +1,43 @@ +package = "LuaFileSystem" +version = "1.4.1rc1-1" +source = { + url = "http://luafilesystem.luaforge.net/luafilesystem-1.4.1rc1.tar.gz", +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} +dependencies = { + "lua >= 5.1" +} +build = { + platforms = { + unix = { + type = "make", + build_variables = { + LIB_OPTION = "$(LIBFLAG)", + CFLAGS = "$(CFLAGS) -I$(LUA_INCDIR) $(STAT64)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)" + } + }, + win32 = { + type = "make", + build_variables = { + LUA_LIB = "$(LUA_LIBDIR)\\lua5.1.lib", + CFLAGS = "/MD $(CFLAGS) /I$(LUA_INCDIR)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)", + LUA_DIR = "$(LUADIR)", + BIN_DIR = "$(BINDIR)" + } + } + } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.4.2-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.4.2-1.rockspec new file mode 100644 index 0000000..7cfe92b --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.4.2-1.rockspec @@ -0,0 +1,26 @@ +package = "LuaFileSystem" + +version = "1.4.2-1" + +source = { + url = "http://luaforge.net/frs/download.php/3931/luafilesystem-1.4.2.tar.gz", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "module", + modules = { lfs = "src/lfs.c" } +} \ No newline at end of file diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.5.0-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.5.0-1.rockspec new file mode 100644 index 0000000..1170ad2 --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.5.0-1.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" + +version = "1.5.0-1" + +source = { + url = "http://cloud.github.com/downloads/keplerproject/luafilesystem/luafilesystem-1.5.0.tar.gz", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "module", + modules = { lfs = "src/lfs.c" }, + copy_directories = { "doc", "tests" } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.6.0-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.6.0-1.rockspec new file mode 100644 index 0000000..82d349c --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.6.0-1.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" + +version = "1.6.0-1" + +source = { + url = "https://github.com/downloads/keplerproject/luafilesystem/luafilesystem-1.6.0.tar.gz", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "builtin", + modules = { lfs = "src/lfs.c" }, + copy_directories = { "doc", "tests" } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.6.1-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.6.1-1.rockspec new file mode 100644 index 0000000..7f45e33 --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.6.1-1.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" + +version = "1.6.1-1" + +source = { + url = "https://github.com/downloads/keplerproject/luafilesystem/luafilesystem-1.6.1.tar.gz", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "builtin", + modules = { lfs = "src/lfs.c" }, + copy_directories = { "doc", "tests" } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.6.2-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.6.2-1.rockspec new file mode 100644 index 0000000..1c11efc --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.6.2-1.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" + +version = "1.6.2-1" + +source = { + url = "https://github.com/downloads/keplerproject/luafilesystem/luafilesystem-1.6.2.tar.gz", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "builtin", + modules = { lfs = "src/lfs.c" }, + copy_directories = { "doc", "tests" } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-1.6.3-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-1.6.3-1.rockspec new file mode 100644 index 0000000..89b25d4 --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-1.6.3-1.rockspec @@ -0,0 +1,28 @@ +package = "LuaFileSystem" +version = "1.6.3-1" +source = { + url = "git://github.com/keplerproject/luafilesystem", + tag = "v_1_6_3", +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]], + license = "MIT/X11", +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + lfs = "src/lfs.c" + }, + copy_directories = { + "doc", "tests" + } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-cvs-1.rockspec b/luaclib/lfs/rockspecs/luafilesystem-cvs-1.rockspec new file mode 100644 index 0000000..a02d4f1 --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-cvs-1.rockspec @@ -0,0 +1,44 @@ +package = "LuaFileSystem" +version = "cvs-1" +source = { + url = "cvs://:pserver:anonymous:@cvs.luaforge.net:/cvsroot/luafilesystem", + cvs_tag = "HEAD" +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} +dependencies = { + "lua >= 5.1" +} +build = { + platforms = { + unix = { + type = "make", + build_variables = { + LIB_OPTION = "$(LIBFLAG)", + CFLAGS = "$(CFLAGS) -I$(LUA_INCDIR)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)" + } + }, + win32 = { + type = "make", + build_variables = { + LUA_LIB = "$(LUA_LIBDIR)\\lua5.1.lib", + CFLAGS = "$(CFLAGS) /I$(LUA_INCDIR)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)", + LUA_DIR = "$(LUADIR)", + BIN_DIR = "$(BINDIR)" + } + } + } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-cvs-2.rockspec b/luaclib/lfs/rockspecs/luafilesystem-cvs-2.rockspec new file mode 100644 index 0000000..651c7cf --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-cvs-2.rockspec @@ -0,0 +1,26 @@ +package = "LuaFileSystem" + +version = "cvs-2" + +source = { + url = "git://github.com/keplerproject/luafilesystem.git", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "module", + modules = { lfs = "src/lfs.c" } +} diff --git a/luaclib/lfs/rockspecs/luafilesystem-cvs-3.rockspec b/luaclib/lfs/rockspecs/luafilesystem-cvs-3.rockspec new file mode 100644 index 0000000..a4388cd --- /dev/null +++ b/luaclib/lfs/rockspecs/luafilesystem-cvs-3.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" + +version = "cvs-3" + +source = { + url = "git://github.com/keplerproject/luafilesystem.git", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1, < 5.4" +} + +build = { + type = "builtin", + modules = { lfs = "src/lfs.c" }, + copy_directories = { "doc", "tests" } +} diff --git a/luaclib/lfs/tests/._test.lua b/luaclib/lfs/tests/._test.lua new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/tests/._test.lua differ diff --git a/luaclib/lfs/tests/test.lua b/luaclib/lfs/tests/test.lua new file mode 100644 index 0000000..abfbd4d --- /dev/null +++ b/luaclib/lfs/tests/test.lua @@ -0,0 +1,175 @@ +#!/usr/bin/env lua5.1 + +local tmp = "/tmp" +local sep = string.match (package.config, "[^\n]+") +local upper = ".." + +local lfs = require"lfs" +print (lfs._VERSION) + +io.write(".") +io.flush() + +function attrdir (path) + for file in lfs.dir(path) do + if file ~= "." and file ~= ".." then + local f = path..sep..file + print ("\t=> "..f.." <=") + local attr = lfs.attributes (f) + assert (type(attr) == "table") + if attr.mode == "directory" then + attrdir (f) + else + for name, value in pairs(attr) do + print (name, value) + end + end + end + end +end + +-- Checking changing directories +local current = assert (lfs.currentdir()) +local reldir = string.gsub (current, "^.*%"..sep.."([^"..sep.."])$", "%1") +assert (lfs.chdir (upper), "could not change to upper directory") +assert (lfs.chdir (reldir), "could not change back to current directory") +assert (lfs.currentdir() == current, "error trying to change directories") +assert (lfs.chdir ("this couldn't be an actual directory") == nil, "could change to a non-existent directory") + +io.write(".") +io.flush() + +-- Changing creating and removing directories +local tmpdir = current..sep.."lfs_tmp_dir" +local tmpfile = tmpdir..sep.."tmp_file" +-- Test for existence of a previous lfs_tmp_dir +-- that may have resulted from an interrupted test execution and remove it +if lfs.chdir (tmpdir) then + assert (lfs.chdir (upper), "could not change to upper directory") + assert (os.remove (tmpfile), "could not remove file from previous test") + assert (lfs.rmdir (tmpdir), "could not remove directory from previous test") +end + +io.write(".") +io.flush() + +-- tries to create a directory +assert (lfs.mkdir (tmpdir), "could not make a new directory") +local attrib, errmsg = lfs.attributes (tmpdir) +if not attrib then + error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg) +end +local f = io.open(tmpfile, "w") +f:close() + +io.write(".") +io.flush() + +-- Change access time +local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0}) +assert (lfs.touch (tmpfile, testdate)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate, "could not set access time") +assert (new_att.modification == testdate, "could not set modification time") + +io.write(".") +io.flush() + +-- Change access and modification time +local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0}) +local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0}) + +assert (lfs.touch (tmpfile, testdate2, testdate1)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate2, "could not set access time") +assert (new_att.modification == testdate1, "could not set modification time") + +io.write(".") +io.flush() + +-- Checking link (does not work on Windows) +if lfs.link (tmpfile, "_a_link_for_test_", true) then + assert (lfs.attributes"_a_link_for_test_".mode == "file") + assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link") + assert (lfs.link (tmpfile, "_a_hard_link_for_test_")) + assert (lfs.attributes (tmpfile, "nlink") == 2) + assert (os.remove"_a_link_for_test_") + assert (os.remove"_a_hard_link_for_test_") +end + +io.write(".") +io.flush() + +-- Checking text/binary modes (only has an effect in Windows) +local f = io.open(tmpfile, "w") +local result, mode = lfs.setmode(f, "binary") +assert(result) -- on non-Windows platforms, mode is always returned as "binary" +result, mode = lfs.setmode(f, "text") +assert(result and mode == "binary") +f:close() + +io.write(".") +io.flush() + +-- Restore access time to current value +assert (lfs.touch (tmpfile, attrib.access, attrib.modification)) +new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == attrib.access) +assert (new_att.modification == attrib.modification) + +io.write(".") +io.flush() + +-- Check consistency of lfs.attributes values +local attr = lfs.attributes (tmpfile) +for key, value in pairs(attr) do + assert (value == lfs.attributes (tmpfile, key), + "lfs.attributes values not consistent") +end + +-- Remove new file and directory +assert (os.remove (tmpfile), "could not remove new file") +assert (lfs.rmdir (tmpdir), "could not remove new directory") +assert (lfs.mkdir (tmpdir..sep.."lfs_tmp_dir") == nil, "could create a directory inside a non-existent one") + +io.write(".") +io.flush() + +-- Trying to get attributes of a non-existent file +assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file") +assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") + +io.write(".") +io.flush() + +-- Stressing directory iterator +count = 0 +for i = 1, 4000 do + for file in lfs.dir (tmp) do + count = count + 1 + end +end + +io.write(".") +io.flush() + +-- Stressing directory iterator, explicit version +count = 0 +for i = 1, 4000 do + local iter, dir = lfs.dir(tmp) + local file = dir:next() + while file do + count = count + 1 + file = dir:next() + end + assert(not pcall(dir.next, dir)) +end + +io.write(".") +io.flush() + +-- directory explicit close +local iter, dir = lfs.dir(tmp) +dir:close() +assert(not pcall(dir.next, dir)) +print"Ok!" diff --git a/luaclib/lfs/vc6/._lfs.def b/luaclib/lfs/vc6/._lfs.def new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/vc6/._lfs.def differ diff --git a/luaclib/lfs/vc6/._luafilesystem.dsw b/luaclib/lfs/vc6/._luafilesystem.dsw new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/vc6/._luafilesystem.dsw differ diff --git a/luaclib/lfs/vc6/._luafilesystem_dll.dsp b/luaclib/lfs/vc6/._luafilesystem_dll.dsp new file mode 100644 index 0000000..240a244 Binary files /dev/null and b/luaclib/lfs/vc6/._luafilesystem_dll.dsp differ diff --git a/luaclib/lfs/vc6/lfs.def b/luaclib/lfs/vc6/lfs.def new file mode 100644 index 0000000..55ec688 --- /dev/null +++ b/luaclib/lfs/vc6/lfs.def @@ -0,0 +1,5 @@ +LIBRARY lfs.dll +DESCRIPTION "LuaFileSystem" +VERSION 1.2 +EXPORTS +luaopen_lfs diff --git a/luaclib/lfs/vc6/luafilesystem.dsw b/luaclib/lfs/vc6/luafilesystem.dsw new file mode 100644 index 0000000..b4bb4b3 --- /dev/null +++ b/luaclib/lfs/vc6/luafilesystem.dsw @@ -0,0 +1,33 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "luafilesystem_dll"=.\luafilesystem_dll.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + luafilesystem + .. + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/luaclib/lfs/vc6/luafilesystem_dll.dsp b/luaclib/lfs/vc6/luafilesystem_dll.dsp new file mode 100644 index 0000000..efe6c72 --- /dev/null +++ b/luaclib/lfs/vc6/luafilesystem_dll.dsp @@ -0,0 +1,127 @@ +# Microsoft Developer Studio Project File - Name="luafilesystem_dll" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=luafilesystem_dll - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "luafilesystem_dll.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "luafilesystem_dll.mak" CFG="luafilesystem_dll - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "luafilesystem_dll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "luafilesystem_dll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "luafilesystem_dll" +# PROP Scc_LocalPath ".." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "luafilesystem_dll - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../lib/vc6" +# PROP Intermediate_Dir "luafilesystem_dll/Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LUAFILESYSTEM_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../external-src/lua50/include" /I "../../compat/src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LUAFILESYSTEM_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x416 /d "NDEBUG" +# ADD RSC /l 0x416 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 lua50.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../bin/vc6/lfs.dll" /libpath:"../../external-src/lua50/lib/dll" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=cd ../bin/vc6 zip.exe luafilesystem-1.2-win32.zip lfs.dll +# End Special Build Tool + +!ELSEIF "$(CFG)" == "luafilesystem_dll - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../lib/vc6" +# PROP Intermediate_Dir "luafilesystem_dll/Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LUAFILESYSTEM_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../external-src/lua50/include" /I "../../compat/src" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LUAFILESYSTEM_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x416 /d "_DEBUG" +# ADD RSC /l 0x416 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 lua50.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../bin/vc6/lfsd.dll" /pdbtype:sept /libpath:"../../external-src/lua50/lib/dll" + +!ENDIF + +# Begin Target + +# Name "luafilesystem_dll - Win32 Release" +# Name "luafilesystem_dll - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE="..\..\compat\src\compat-5.1.c" +# End Source File +# Begin Source File + +SOURCE=..\src\lfs.c +# End Source File +# Begin Source File + +SOURCE=.\lfs.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE="..\..\compat\src\compat-5.1.h" +# End Source File +# Begin Source File + +SOURCE=..\src\lfs.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/luaclib/lpeg/HISTORY b/luaclib/lpeg/HISTORY new file mode 100644 index 0000000..0c10edd --- /dev/null +++ b/luaclib/lpeg/HISTORY @@ -0,0 +1,96 @@ +HISTORY for LPeg 1.0 + +* Changes from version 0.12 to 1.0 + --------------------------------- + + 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/lpeg/Makefile b/luaclib/lpeg/Makefile new file mode 100644 index 0000000..d008340 --- /dev/null +++ b/luaclib/lpeg/Makefile @@ -0,0 +1,62 @@ +.PHONY: all clean +CC = gcc +RM = rm -rf +##PLATFORM ?= linux + +TARGET = lpeg.so +all: $(TARGET) + +## source file path +SRC_PATH := . + +## used headers file path +INCLUDE_PATH := ../../deps/lua + +## used include librarys file path +LIBRARY_PATH := ../../deps/lua + +## need libs, add at here +LIBS := nlua + +## define CFLAGS +CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -std=c99 -fpic -D LUA_COMPAT_5_2 -D LUA_COMPAT_5_1 +ifeq (RELEASE,$(RELEASE)) +CFLAGS += -D RELEASE -O3 +endif + +## define LDFLAGS +LDFLAGS += -shared + +ifeq (darwin,$(PLATFORM)) +LDFLAGS += -undefined dynamic_lookup \ + -framework Foundation \ + -framework CoreServices \ + -framework ApplicationServices +endif + +ifeq (linux,$(PLATFORM)) +LIBS += dl +endif + + +## get all source files +SRCS += $(wildcard $(SRC_PATH)/*.c) + +## all .o based on all .c +## OBJS := $(SRCS:.c=.o) +OBJS = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o + +## get all include path +CFLAGS += $(foreach dir, $(INCLUDE_PATH), -I$(dir)) +.c.o: + $(CC) -c $(CFLAGS) -o $@ $< + +## get all library path +LDFLAGS += $(foreach lib, $(LIBRARY_PATH), -L$(lib)) +## get all librarys +LDFLAGS += $(foreach lib, $(LIBS), -l$(lib)) +$(TARGET): $(OBJS) + $(CC) -o $@ $(OBJS) $(LDFLAGS) + +clean: + $(RM) *.o $(TARGET) diff --git a/luaclib/lpeg/lpcap.c b/luaclib/lpeg/lpcap.c new file mode 100644 index 0000000..c9085de --- /dev/null +++ b/luaclib/lpeg/lpcap.c @@ -0,0 +1,537 @@ +/* +** $Id: lpcap.c,v 1.6 2015/06/15 16:09:57 roberto Exp $ +** 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, including the initial Cgroup. (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); + 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; /* number of captures of all kinds 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; + } + } +} + + +/* +** 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; + luaL_checkstack(L, 4, "too many captures"); + switch (captype(cs->cap)) { + case Cposition: { + lua_pushinteger(L, cs->cap->s - cs->s + 1); + cs->cap++; + return 1; + } + case Cconst: { + pushluaval(cs); + cs->cap++; + return 1; + } + 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); + return 1; + } + case Csimple: { + int k = pushnestedvalues(cs, 1); + lua_insert(L, -k); /* make whole match be first result */ + return k; + } + case Cruntime: { + lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */ + return 1; + } + case Cstring: { + luaL_Buffer b; + luaL_buffinit(L, &b); + stringcap(&b, cs); + luaL_pushresult(&b); + return 1; + } + case Csubst: { + luaL_Buffer b; + luaL_buffinit(L, &b); + substcap(&b, cs); + luaL_pushresult(&b); + return 1; + } + case Cgroup: { + if (cs->cap->idx == 0) /* anonymous group? */ + return pushnestedvalues(cs, 0); /* add all nested values */ + else { /* named group: add no values */ + nextcap(cs); /* skip capture */ + return 0; + } + } + case Cbackref: return backrefcap(cs); + case Ctable: return tablecap(cs); + case Cfunction: return functioncap(cs); + case Cnum: return numcap(cs); + case Cquery: return querycap(cs); + case Cfold: return foldcap(cs); + default: assert(0); return 0; + } +} + + +/* +** 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.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/lpeg/lpcap.h b/luaclib/lpeg/lpcap.h new file mode 100644 index 0000000..d762fdc --- /dev/null +++ b/luaclib/lpeg/lpcap.h @@ -0,0 +1,43 @@ +/* +** $Id: lpcap.h,v 1.2 2015/02/27 17:13:17 roberto Exp $ +*/ + +#if !defined(lpcap_h) +#define lpcap_h + + +#include "lptypes.h" + + +/* kinds of captures */ +typedef enum CapKind { + Cclose, Cposition, Cconst, Cbackref, Carg, Csimple, Ctable, Cfunction, + Cquery, Cstring, Cnum, Csubst, Cfold, Cruntime, Cgroup +} 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 */ +} 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/lpeg/lpcode.c b/luaclib/lpeg/lpcode.c new file mode 100644 index 0000000..fbf44fe --- /dev/null +++ b/luaclib/lpeg/lpcode.c @@ -0,0 +1,986 @@ +/* +** $Id: lpcode.c,v 1.23 2015/06/12 18:36:47 roberto Exp $ +** 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; + } +} + + +/* +** Check whether a pattern tree has captures +*/ +int hascaptures (TTree *tree) { + tailcall: + switch (tree->tag) { + case TCapture: case TRunTime: + return 1; + case TCall: + tree = sib2(tree); goto tailcall; /* return hascaptures(sib2(tree)); */ + 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) +** ('count' avoids infinite loops for grammars) +*/ +int fixedlenx (TTree *tree, int count, int len) { + 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 fixedlenx(sib1(tree), count); */ + tree = sib1(tree); goto tailcall; + case TCall: + if (count++ >= MAXRULES) + return -1; /* may be a loop */ + /* else return fixedlenx(sib2(tree), count); */ + tree = sib2(tree); goto tailcall; + case TSeq: { + len = fixedlenx(sib1(tree), count, len); + if (len < 0) return -1; + /* else return fixedlenx(sib2(tree), count, len); */ + tree = sib2(tree); goto tailcall; + } + case TChoice: { + int n1, n2; + n1 = fixedlenx(sib1(tree), count, len); + if (n1 < 0) return -1; + n2 = fixedlenx(sib2(tree), count, len); + if (n1 == n2) return n1; + else return -1; + } + 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, 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/lpeg/lpcode.h b/luaclib/lpeg/lpcode.h new file mode 100644 index 0000000..896d3c7 --- /dev/null +++ b/luaclib/lpeg/lpcode.h @@ -0,0 +1,42 @@ +/* +** $Id: lpcode.h,v 1.7 2015/06/12 18:24:45 roberto Exp $ +*/ + +#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 fixedlenx (TTree *tree, int count, int len); +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) + +#define fixedlen(t) fixedlenx(t, 0, 0) + + + +#endif diff --git a/luaclib/lpeg/lpeg-128.gif b/luaclib/lpeg/lpeg-128.gif new file mode 100644 index 0000000..bbf5e78 Binary files /dev/null and b/luaclib/lpeg/lpeg-128.gif differ diff --git a/luaclib/lpeg/lpeg.def b/luaclib/lpeg/lpeg.def new file mode 100644 index 0000000..9a2fb87 --- /dev/null +++ b/luaclib/lpeg/lpeg.def @@ -0,0 +1,3 @@ +LIBRARY + EXPORTS + luaopen_lpeg \ No newline at end of file diff --git a/luaclib/lpeg/lpeg.html b/luaclib/lpeg/lpeg.html new file mode 100644 index 0000000..c0a7f09 --- /dev/null +++ b/luaclib/lpeg/lpeg.html @@ -0,0 +1,1434 @@ + + + + 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 creates values +(the so called semantic information) when 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 every time it succeeds. +For instance, +a capture inside a loop produces as many values as matched by the loop. +A capture produces a value 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). +

+ +

+Usually, +LPeg evaluates all captures only after (and if) the entire match succeeds. +During match time it only gathers enough information +to produce the capture values later. +As a particularly important consequence, +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. +

+ + +

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 creates a table and puts 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. +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-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. +

+ +
+ +
+ +
+

+$Id: lpeg.html,v 1.75 2015/09/28 17:17:41 roberto Exp $ +

+
+ +
+ + + diff --git a/luaclib/lpeg/lpeg.vcxproj b/luaclib/lpeg/lpeg.vcxproj new file mode 100644 index 0000000..eaa8d9d --- /dev/null +++ b/luaclib/lpeg/lpeg.vcxproj @@ -0,0 +1,94 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8} + lpeg + + + + DynamicLibrary + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(ProjectDir) + + + + Level3 + Disabled + true + ..\..\deps\lua; + WIN32;_DEBUG;_WINDOWS;_USRDLL;LUA_CORE;LUA_BUILD_AS_DLL;LUA_COMPAT_5_2;LUA_COMPAT_5_1;%(PreprocessorDefinitions) + + + true + ..\..\deps\lua; + nlua.lib + lpeg.def + + + copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir) + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/luaclib/lpeg/lpeg.vcxproj.filters b/luaclib/lpeg/lpeg.vcxproj.filters new file mode 100644 index 0000000..7171297 --- /dev/null +++ b/luaclib/lpeg/lpeg.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {0c90a889-7060-4f4c-9513-678c730b15bf} + + + + + src + + + src + + + src + + + src + + + src + + + + + src + + + src + + + src + + + src + + + src + + + src + + + \ No newline at end of file diff --git a/luaclib/lpeg/lpeg.vcxproj.user b/luaclib/lpeg/lpeg.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/luaclib/lpeg/lpeg.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/luaclib/lpeg/lpprint.c b/luaclib/lpeg/lpprint.c new file mode 100644 index 0000000..174d168 --- /dev/null +++ b/luaclib/lpeg/lpprint.c @@ -0,0 +1,244 @@ +/* +** $Id: lpprint.c,v 1.9 2015/06/15 16:09:57 roberto Exp $ +** 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 void printcapkind (int kind) { + const char *const modes[] = { + "close", "position", "constant", "backref", + "argument", "simple", "table", "function", + "query", "string", "num", "substitution", "fold", + "runtime", "group"}; + printf("%s", 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: { + printcapkind(getkind(p)); + printf(" (size = %d) (idx = %d)", getoff(p), p->i.key); + break; + } + case IOpenCapture: { + printcapkind(getkind(p)); + printf(" (idx = %d)", 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) { + printcapkind(cap->kind); + printf(" (idx: %d - size: %d) -> %p\n", 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: { + printf(" key: %d\n", tree->key); + break; + } + case TBehind: { + printf(" %d\n", tree->u.n); + printtree(sib1(tree), ident + 2); + break; + } + case TCapture: { + printf(" cap: %d key: %d n: %d\n", tree->cap, tree->key, tree->u.n); + 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/lpeg/lpprint.h b/luaclib/lpeg/lpprint.h new file mode 100644 index 0000000..6329760 --- /dev/null +++ b/luaclib/lpeg/lpprint.h @@ -0,0 +1,36 @@ +/* +** $Id: lpprint.h,v 1.2 2015/06/12 18:18:08 roberto Exp $ +*/ + + +#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/lpeg/lptree.c b/luaclib/lpeg/lptree.c new file mode 100644 index 0000000..ac5f515 --- /dev/null +++ b/luaclib/lpeg/lptree.c @@ -0,0 +1,1296 @@ +/* +** $Id: lptree.c,v 1.21 2015/09/28 17:01:25 roberto Exp $ +** 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; +} + + +/* +** 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. +*/ +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) { + return auxemptycap(newtree(L, 2), cap); +} + + +/* +** 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); + return 1; +} + + +static int lp_argcapture (lua_State *L) { + int n = (int)luaL_checkinteger(L, 1); + TTree *tree = newemptycap(L, Carg); + tree->key = n; + luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index"); + 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; + 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; + } + } +} + + +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.) +** 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/lpeg/lptree.h b/luaclib/lpeg/lptree.h new file mode 100644 index 0000000..b69528a --- /dev/null +++ b/luaclib/lpeg/lptree.h @@ -0,0 +1,77 @@ +/* +** $Id: lptree.h,v 1.2 2013/03/24 13:51:12 roberto Exp $ +*/ + +#if !defined(lptree_h) +#define lptree_h + + +#include "lptypes.h" + + +/* +** types of trees +*/ +typedef enum TTag { + TChar = 0, TSet, TAny, /* standard PEG elements */ + TTrue, TFalse, + TRep, + TSeq, TChoice, + TNot, TAnd, + TCall, + TOpenCall, + TRule, /* sib1 is rule's pattern, sib2 is 'next' rule */ + TGrammar, /* sib1 is initial (and first) rule */ + TBehind, /* match behind */ + TCapture, /* regular capture */ + TRunTime /* run-time capture */ +} TTag; + +/* number of siblings for each tree */ +extern const byte numsiblings[]; + + +/* +** Tree trees +** The first sibling of a tree (if there is one) is immediately after +** the tree. A reference to a second sibling (ps) is its position +** relative to the position of the tree itself. A key in ktable +** uses the (unique) address of the original tree that created that +** entry. NULL means no data. +*/ +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 sibling */ + 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 siblings for each tree */ +extern const byte numsiblings[]; + +/* access to siblings */ +#define sib1(t) ((t) + 1) +#define sib2(t) ((t) + (t)->u.ps) + + + + + + +#endif + diff --git a/luaclib/lpeg/lptypes.h b/luaclib/lpeg/lptypes.h new file mode 100644 index 0000000..5eb7987 --- /dev/null +++ b/luaclib/lpeg/lptypes.h @@ -0,0 +1,149 @@ +/* +** $Id: lptypes.h,v 1.14 2015/09/28 17:17:41 roberto Exp $ +** LPeg - PEG pattern matching for Lua +** Copyright 2007-2015, Lua.org & PUC-Rio (see 'lpeg.html' for license) +** written by Roberto Ierusalimschy +*/ + +#if !defined(lptypes_h) +#define lptypes_h + + +#if !defined(LPEG_DEBUG) +#define NDEBUG +#endif + +#include +#include + +#include "lua.h" + + +#define VERSION "1.0.0" + + +#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 */ +#if !defined(MAXRULES) +#define MAXRULES 1000 +#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/lpeg/lpvm.c b/luaclib/lpeg/lpvm.c new file mode 100644 index 0000000..eaf2ebf --- /dev/null +++ b/luaclib/lpeg/lpvm.c @@ -0,0 +1,355 @@ +/* +** $Id: lpvm.c,v 1.6 2015/09/28 17:01:25 roberto Exp $ +** 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))) + + +/* +** Double the size of the array of captures +*/ +static Capture *doublecap (lua_State *L, Capture *cap, int captop, int ptop) { + Capture *newc; + if (captop >= INT_MAX/((int)sizeof(Capture) * 2)) + luaL_error(L, "too many captures"); + newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture)); + memcpy(newc, cap, captop * sizeof(Capture)); + 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 capture list +** 'base', nested inside a group capture. 'fd' indexes the first capture +** value, 'n' is the number of values (at least 1). +*/ +static void adddyncaptures (const char *s, Capture *base, int n, int fd) { + int i; + /* Cgroup capture is already there */ + assert(base[0].kind == Cgroup && base[0].siz == 0); + base[0].idx = 0; /* make it an anonymous group */ + for (i = 1; i <= n; i++) { /* add runtime captures */ + base[i].kind = Cruntime; + base[i].siz = 1; /* mark it as closed */ + base[i].idx = fd + i - 1; /* stack index of capture value */ + base[i].s = s; + } + base[i].kind = Cclose; /* close group */ + base[i].siz = 1; + base[i].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("s: |%s| stck:%d, dyncaps:%d, caps:%d ", + s, stack - getstackbase(L, ptop), ndyncap, captop); + printinst(op, p); + printcaplist(capture, capture + captop); +#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; + continue; + } + case ICloseRunTime: { + CapState cs; + int rem, res, n; + int fr = lua_gettop(L) + 1; /* stack index of first result */ + cs.s = o; cs.L = L; cs.ocap = capture; cs.ptop = ptop; + n = runtimecap(&cs, capture + captop, s, &rem); /* call function */ + captop -= n; /* remove nested 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 - rem; /* update number of dynamic captures */ + if (n > 0) { /* any new capture? */ + if ((captop += n + 2) >= capsize) { + capture = doublecap(L, capture, captop, ptop); + capsize = 2 * captop; + } + /* add new captures to 'capture' list */ + adddyncaptures(s, capture + captop - n - 2, n, fr); + } + 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); + if (++captop >= capsize) { + capture = doublecap(L, capture, captop, ptop); + capsize = 2 * captop; + } + p++; + continue; + } + default: assert(0); return NULL; + } + } +} + +/* }====================================================== */ + + diff --git a/luaclib/lpeg/lpvm.h b/luaclib/lpeg/lpvm.h new file mode 100644 index 0000000..757b9e1 --- /dev/null +++ b/luaclib/lpeg/lpvm.h @@ -0,0 +1,58 @@ +/* +** $Id: lpvm.h,v 1.3 2014/02/21 13:06:41 roberto Exp $ +*/ + +#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/lpeg/re.html b/luaclib/lpeg/re.html new file mode 100644 index 0000000..d0d9744 --- /dev/null +++ b/luaclib/lpeg/re.html @@ -0,0 +1,498 @@ + + + + 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 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 (alternative / grammar)
+
+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. +

+ +
+ +
+ +
+

+$Id: re.html,v 1.23 2015/09/28 17:17:41 roberto Exp $ +

+
+ +
+ + + diff --git a/luaclib/lpeg/re.lua b/luaclib/lpeg/re.lua new file mode 100644 index 0000000..3b9974f --- /dev/null +++ b/luaclib/lpeg/re.lua @@ -0,0 +1,259 @@ +-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $ + +-- 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 getdef (id, defs) + local c = defs and defs[id] + if not c then error("undefined name: " .. id) end + return c +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 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) + +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) + + m.Cg(Def / getdef * m.Cc(mt.__div)) + ) + + "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt)) + ) * 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/lpeg/test.lua b/luaclib/lpeg/test.lua new file mode 100644 index 0000000..017a3ab --- /dev/null +++ b/luaclib/lpeg/test.lua @@ -0,0 +1,1448 @@ +#!/usr/bin/env lua + +-- $Id: test.lua,v 1.109 2015/09/28 17:01:25 roberto Exp $ + +-- 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 + + +-- 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 + + +-- 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.") + +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 + +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) +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'}}) + + +-- 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" + + diff --git a/luaclib/luaclib.sln b/luaclib/luaclib.sln new file mode 100644 index 0000000..bac854d --- /dev/null +++ b/luaclib/luaclib.sln @@ -0,0 +1,80 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cjson", "cjson\cjson.vcxproj", "{933BAD43-4B46-4BC1-A943-7825C5946B23}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lfs", "lfs\lfs.vcxproj", "{BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lpeg", "lpeg\lpeg.vcxproj", "{3350DD32-C039-442E-94D5-DAB4E4A0A4C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mime", "mime\mime.vcxproj", "{2691C520-B01B-46AB-B585-716767F72A9F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "protobuf", "pb\pbc.vcxproj", "{EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcxproj", "{F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|ARM = Release|ARM + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Debug|ARM.ActiveCfg = Debug|Win32 + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Debug|Win32.ActiveCfg = Debug|Win32 + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Debug|Win32.Build.0 = Debug|Win32 + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Debug|x64.ActiveCfg = Debug|Win32 + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Release|ARM.ActiveCfg = Release|Win32 + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Release|Win32.ActiveCfg = Release|Win32 + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Release|Win32.Build.0 = Release|Win32 + {933BAD43-4B46-4BC1-A943-7825C5946B23}.Release|x64.ActiveCfg = Release|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Debug|ARM.ActiveCfg = Debug|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Debug|Win32.ActiveCfg = Debug|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Debug|Win32.Build.0 = Debug|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Debug|x64.ActiveCfg = Debug|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Release|ARM.ActiveCfg = Release|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Release|Win32.ActiveCfg = Release|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Release|Win32.Build.0 = Release|Win32 + {BB87BCAD-A936-4A83-8A9C-1BCFE8B7393E}.Release|x64.ActiveCfg = Release|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Debug|ARM.ActiveCfg = Debug|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Debug|Win32.Build.0 = Debug|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Debug|x64.ActiveCfg = Debug|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Release|ARM.ActiveCfg = Release|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Release|Win32.ActiveCfg = Release|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Release|Win32.Build.0 = Release|Win32 + {3350DD32-C039-442E-94D5-DAB4E4A0A4C8}.Release|x64.ActiveCfg = Release|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Debug|ARM.ActiveCfg = Debug|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Debug|Win32.ActiveCfg = Debug|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Debug|Win32.Build.0 = Debug|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Debug|x64.ActiveCfg = Debug|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Release|ARM.ActiveCfg = Release|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Release|Win32.ActiveCfg = Release|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Release|Win32.Build.0 = Release|Win32 + {2691C520-B01B-46AB-B585-716767F72A9F}.Release|x64.ActiveCfg = Release|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Debug|ARM.ActiveCfg = Debug|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Debug|Win32.ActiveCfg = Debug|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Debug|Win32.Build.0 = Debug|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Debug|x64.ActiveCfg = Debug|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Release|ARM.ActiveCfg = Release|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Release|Win32.ActiveCfg = Release|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Release|Win32.Build.0 = Release|Win32 + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE}.Release|x64.ActiveCfg = Release|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Debug|ARM.ActiveCfg = Debug|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Debug|Win32.ActiveCfg = Debug|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Debug|Win32.Build.0 = Debug|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Debug|x64.ActiveCfg = Debug|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Release|ARM.ActiveCfg = Release|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Release|Win32.ActiveCfg = Release|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Release|Win32.Build.0 = Release|Win32 + {F09D4DFA-1E4E-4D19-8FBD-95F602ACC371}.Release|x64.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/clib/mime/Makefile b/luaclib/mime/Makefile similarity index 90% rename from clib/mime/Makefile rename to luaclib/mime/Makefile index 5614969..d5634a4 100644 --- a/clib/mime/Makefile +++ b/luaclib/mime/Makefile @@ -19,9 +19,9 @@ LIBRARY_PATH := ../../deps/lua LIBS := nlua ## define CFLAGS -CFLAGS += -g -O3 -Wall -Wextra -Wno-unused-parameter -fpic -D LUA_COMPAT_ALL +CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -fpic -D LUA_COMPAT_5_2 -D LUA_COMPAT_5_1 ifeq (RELEASE,$(RELEASE)) -CFLAGS += -D RELEASE +CFLAGS += -D RELEASE -O3 endif ## define LDFLAGS diff --git a/clib/mime/mime.c b/luaclib/mime/mime.c similarity index 100% rename from clib/mime/mime.c rename to luaclib/mime/mime.c diff --git a/clib/mime/mime.h b/luaclib/mime/mime.h similarity index 100% rename from clib/mime/mime.h rename to luaclib/mime/mime.h diff --git a/clib/mime/mime.vcxproj b/luaclib/mime/mime.vcxproj similarity index 95% rename from clib/mime/mime.vcxproj rename to luaclib/mime/mime.vcxproj index a99b86d..276da6f 100644 --- a/clib/mime/mime.vcxproj +++ b/luaclib/mime/mime.vcxproj @@ -57,7 +57,7 @@ NotUsing Level3 Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;LUA_CORE;LUA_BUILD_AS_DLL;LUA_COMPAT_MODULE;MIME_EXPORTS;%(PreprocessorDefinitions) + WIN32;_DEBUG;_WINDOWS;_USRDLL;LUA_CORE;LUA_BUILD_AS_DLL;LUA_COMPAT_5_2;LUA_COMPAT_5_1;MIME_EXPORTS;%(PreprocessorDefinitions) true ..\..\deps\lua; @@ -68,7 +68,7 @@ ..\..\deps\lua; - $(SolutionDir)install.bat $(SolutionDir) $(ProjectName) + copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir) diff --git a/clib/mime/mime.vcxproj.filters b/luaclib/mime/mime.vcxproj.filters similarity index 100% rename from clib/mime/mime.vcxproj.filters rename to luaclib/mime/mime.vcxproj.filters diff --git a/luaclib/mime/mime.vcxproj.user b/luaclib/mime/mime.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/luaclib/mime/mime.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/luaclib/pb/Android.mk b/luaclib/pb/Android.mk new file mode 100644 index 0000000..de8d38f --- /dev/null +++ b/luaclib/pb/Android.mk @@ -0,0 +1,29 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := pbc + +LOCAL_MODULE_FILENAME := libpbc + +LOCAL_SRC_FILES := \ + src/alloc.c \ +src/array.c \ +src/bootstrap.c \ +src/context.c \ +src/decode.c \ +src/map.c \ +src/pattern.c \ +src/proto.c \ +src/register.c \ +src/rmessage.c \ +src/stringpool.c \ +src/varint.c \ +src/wmessage.c \ + + + +LOCAL_C_INCLUDES+= src\ + + +include $(BUILD_STATIC_LIBRARY) diff --git a/luaclib/pb/Makefile b/luaclib/pb/Makefile new file mode 100644 index 0000000..abcd574 --- /dev/null +++ b/luaclib/pb/Makefile @@ -0,0 +1,62 @@ +.PHONY: all clean +CC = gcc +RM = rm -rf +##PLATFORM ?= linux + +TARGET = protobuf.so +all: $(TARGET) + +## source file path +SRC_PATH := . + +## used headers file path +INCLUDE_PATH := ../../deps/lua src + +## used include librarys file path +LIBRARY_PATH := ../../deps/lua + +## need libs, add at here +LIBS := nlua + +## define CFLAGS +CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -fpic -D LUA_COMPAT_5_2 -D LUA_COMPAT_5_1 +ifeq (RELEASE,$(RELEASE)) +CFLAGS += -D RELEASE -O3 +endif + +## define LDFLAGS +LDFLAGS += -shared + +ifeq (darwin,$(PLATFORM)) +LDFLAGS += -undefined dynamic_lookup \ + -framework Foundation \ + -framework CoreServices \ + -framework ApplicationServices +endif + +ifeq (linux,$(PLATFORM)) +LIBS += dl +endif + + +## get all source files +SRCS += $(wildcard $(SRC_PATH)/*.c) + +## all .o based on all .c +## OBJS := $(SRCS:.c=.o) +OBJS = binding/lua/pbc-lua.o src/context.o src/varint.o src/array.o src/pattern.o src/register.o src/proto.o src/map.o src/alloc.o src/rmessage.o src/wmessage.o src/bootstrap.o src/stringpool.o src/decode.o + +## get all include path +CFLAGS += $(foreach dir, $(INCLUDE_PATH), -I$(dir)) +.c.o: + $(CC) -c $(CFLAGS) -o $@ $< + +## get all library path +LDFLAGS += $(foreach lib, $(LIBRARY_PATH), -L$(lib)) +## get all librarys +LDFLAGS += $(foreach lib, $(LIBS), -l$(lib)) +$(TARGET): $(OBJS) + $(CC) -o $@ $(OBJS) $(LDFLAGS) + +clean: + $(RM) *.o src/*.o binding/lua/*.o $(TARGET) diff --git a/luaclib/pb/Makefile.bak b/luaclib/pb/Makefile.bak new file mode 100644 index 0000000..63f7805 --- /dev/null +++ b/luaclib/pb/Makefile.bak @@ -0,0 +1,84 @@ +CC = gcc +CFLAGS = -O2 +AR = ar rc + +BUILD = build + +.PHONY : all lib clean tool + +LIBSRCS = context.c varint.c array.c pattern.c register.c proto.c map.c alloc.c rmessage.c wmessage.c bootstrap.c stringpool.c decode.c +LIBNAME = libpbc.a + +TESTSRCS = addressbook.c pattern.c pbc.c float.c map.c test.c decode.c +PROTOSRCS = addressbook.proto descriptor.proto float.proto test.proto + +BUILD_O = $(BUILD)/o + +all : lib test + +lib : $(LIBNAME) + +clean : + rm -rf $(BUILD) + +$(BUILD) : $(BUILD_O) + +$(BUILD_O) : + mkdir -p $@ + +TOOL := $(BUILD)/dump + +tool : $(TOOL) + +$(TOOL) : | $(BUILD) +$(TOOL) : $(LIBNAME) +$(TOOL) : tool/dump.c + cd $(BUILD) && $(CC) $(CFLAGS) -I.. -L. -o dump ../$< -lpbc + +LIB_O := + +define BUILD_temp + TAR := $(BUILD_O)/$(notdir $(basename $(1))) + LIB_O := $(LIB_O) $$(TAR).o + $$(TAR).o : | $(BUILD_O) + -include $$(TAR).d + $$(TAR).o : src/$(1) + $(CC) $(CFLAGS) -c -Isrc -I. -o $$@ -MMD $$< +endef + +$(foreach s,$(LIBSRCS),$(eval $(call BUILD_temp,$(s)))) + +$(LIBNAME) : $(LIB_O) + cd $(BUILD) && $(AR) $(LIBNAME) $(addprefix ../,$^) + +TEST := + +define TEST_temp + TAR := $(BUILD)/$(notdir $(basename $(1))) + TEST := $(TEST) $$(TAR) + $$(TAR) : | $(BUILD) + $$(TAR) : $(LIBNAME) + $$(TAR) : test/$(1) + cd $(BUILD) && $(CC) $(CFLAGS) -I.. -L. -o $$(notdir $$@) ../$$< -lpbc +endef + +$(foreach s,$(TESTSRCS),$(eval $(call TEST_temp,$(s)))) + +test : $(TEST) proto + +PROTO := + +define PROTO_temp + TAR := $(BUILD)/$(notdir $(basename $(1))) + PROTO := $(PROTO) $$(TAR).pb + $$(TAR).pb : | $(BUILD) + $$(TAR).pb : test/$(1) + protoc -o$$@ $$< +endef + +$(foreach s,$(PROTOSRCS),$(eval $(call PROTO_temp,$(s)))) + +proto : $(PROTO) + +.PHONY : all lib test proto clean + diff --git a/luaclib/pb/README.md b/luaclib/pb/README.md new file mode 100644 index 0000000..19e4624 --- /dev/null +++ b/luaclib/pb/README.md @@ -0,0 +1,85 @@ +## PBC + +PBC is a google protocol buffers library for C without code generation. + +## Quick Example + + package tutorial; + + message Person { + required string name = 1; + required int32 id = 2; // Unique ID number for this person. + optional string email = 3; + + enum PhoneType { + MOBILE = 0; + HOME = 1; + WORK = 2; + } + + message PhoneNumber { + required string number = 1; + optional PhoneType type = 2 [default = HOME]; + } + + repeated PhoneNumber phone = 4; + } + +```C +struct pbc_rmessage * m = pbc_rmessage_new(env, "tutorial.Person", slice); +printf("name = %s\n", pbc_rmessage_string(m , "name" , 0 , NULL)); +printf("id = %d\n", pbc_rmessage_integer(m , "id" , 0 , NULL)); +printf("email = %s\n", pbc_rmessage_string(m , "email" , 0 , NULL)); + +int phone_n = pbc_rmessage_size(m, "phone"); +int i; + +for (i=0;i +//#include + +#ifndef _MSC_VER +#include +#else +#define alloca _alloca +#endif + +#include +#include +#include + +#include "pbc.h" + +#if LUA_VERSION_NUM == 501 + +#define lua_rawlen lua_objlen +#define luaL_newlib(L ,reg) luaL_register(L,"protobuf.c",reg) +#define luaL_buffinit(L , _ ) +#define luaL_prepbuffsize( b , cap ) malloc(cap) +#define _Free(p) free(p) +#undef luaL_addsize +#define luaL_addsize(b , len) lua_pushlstring(L, temp , len) ; free(temp) +#define luaL_pushresult(b) +#define luaL_checkversion(L) + +#else + +#define _Free(p) + +#endif + +static inline void * +checkuserdata(lua_State *L, int index) { + void * ud = lua_touserdata(L,index); + if (ud == NULL) { + luaL_error(L, "userdata %d is nil",index); + } + return ud; +} + +static int +_env_new(lua_State *L) { + struct pbc_env * env = pbc_new(); + lua_pushlightuserdata(L, env); + return 1; +} + +static int +_env_register(lua_State *L) { + struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1); + size_t sz = 0; + const char * buffer = luaL_checklstring(L, 2 , &sz); + struct pbc_slice slice; + slice.buffer = (void *)buffer; + slice.len = (int)sz; + int ret = pbc_register(env, &slice); + + if (ret) { + return luaL_error(L, "register fail"); + } + return 0; +} + +static int +_rmessage_new(lua_State *L) { + struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1); + const char * type_name = luaL_checkstring(L,2); + struct pbc_slice slice; + if (lua_isstring(L,3)) { + size_t sz = 0; + slice.buffer = (void *)lua_tolstring(L,3,&sz); + slice.len = (int)sz; + } else { + slice.buffer = lua_touserdata(L,3); + slice.len = luaL_checkinteger(L,4); + } + struct pbc_rmessage * m = pbc_rmessage_new(env, type_name, &slice); + if (m==NULL) + return 0; + lua_pushlightuserdata(L,m); + return 1; +} + +static int +_rmessage_delete(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + pbc_rmessage_delete(m); + + return 0; +} + +static int +_rmessage_integer(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = luaL_checkinteger(L,3); + int32_t v = (int32_t)pbc_rmessage_integer(m, key, index, NULL); + + lua_pushinteger(L,v); + + return 1; +} + +static int +_rmessage_int32(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = luaL_checkinteger(L,3); + uint32_t v = pbc_rmessage_integer(m, key, index, NULL); + lua_pushlightuserdata(L,(void *)(intptr_t)v); + + return 1; +} + + +static int +_rmessage_int64(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = luaL_checkinteger(L,3); + uint32_t v[2]; + v[0] = pbc_rmessage_integer(m, key, index, &v[1]); + + lua_pushlstring(L,(const char *)v,sizeof(v)); + + return 1; +} + +static int +_rmessage_int52(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = luaL_checkinteger(L,3); + uint32_t hi,low; + low = pbc_rmessage_integer(m, key, index, &hi); + int64_t v = (int64_t)((uint64_t)hi << 32 | (uint64_t)low); + lua_pushnumber(L,(lua_Number)v); + + return 1; +} + +static int +_rmessage_uint52(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = luaL_checkinteger(L,3); + uint32_t hi,low; + low = pbc_rmessage_integer(m, key, index, &hi); + uint64_t v = (uint64_t)hi << 32 | (uint64_t)low; + lua_pushnumber(L,(lua_Number)v); + + return 1; +} + +static int +_rmessage_real(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = luaL_checkinteger(L,3); + double v = pbc_rmessage_real(m, key, index); + + lua_pushnumber(L,v); + + return 1; +} + +static int +_rmessage_string(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = lua_tointeger(L,3); + int sz = 0; + const char * v = pbc_rmessage_string(m,key,index,&sz); + lua_pushlstring(L,v,sz); + return 1; +} + +static int +_rmessage_message(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int index = lua_tointeger(L,3); + struct pbc_rmessage * v = pbc_rmessage_message(m,key,index); + lua_pushlightuserdata(L,v); + return 1; +} + +static int +_rmessage_size(lua_State *L) { + struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + + int sz = pbc_rmessage_size(m, key); + + lua_pushinteger(L, sz); + + return 1; +} + +static int +_env_type(lua_State *L) { + lua_settop(L,3); + struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1); + const char * type_name = luaL_checkstring(L,2); + if (lua_isnil(L,3)) { + int ret = pbc_type(env, type_name, NULL, NULL); + lua_pushboolean(L,ret); + return 1; + } + const char * key = luaL_checkstring(L,3); + const char * type = NULL; + int ret = pbc_type(env, type_name, key, &type); + lua_pushinteger(L,ret); + if (type == NULL) { + return 1; + } { + lua_pushstring(L, type); + return 2; + } +} + +static int +_wmessage_new(lua_State *L) { + struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1); + const char * type_name = luaL_checkstring(L,2); + struct pbc_wmessage * ret = pbc_wmessage_new(env, type_name); + lua_pushlightuserdata(L,ret); + return 1; +} + +static int +_wmessage_delete(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)lua_touserdata(L,1); + pbc_wmessage_delete(m); + + return 0; +} + + +static int +_wmessage_integer(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int number = luaL_checkinteger(L,3); + uint32_t hi = 0; + if (number < 0) + hi = ~0; + pbc_wmessage_integer(m, key, number, hi); + + return 0; +} + +static int +_wmessage_real(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + double number = luaL_checknumber(L,3); + pbc_wmessage_real(m, key, number); + + return 0; +} + +static int +_wmessage_string(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + size_t len = 0; + const char * v = luaL_checklstring(L,3,&len); + int err = pbc_wmessage_string(m, key, v, (int)len); + if (err) { + return luaL_error(L, "Write string error : %s", v); + } + + return 0; +} + +static int +_wmessage_message(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + struct pbc_wmessage * ret = pbc_wmessage_message(m, key); + lua_pushlightuserdata(L, ret); + + return 1; +} + +static int +_wmessage_int32(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + if (!lua_islightuserdata(L,3)) { + return luaL_error(L,"Need a lightuserdata for int32"); + } + void *number = lua_touserdata(L,3); + pbc_wmessage_integer(m, key, (uint32_t)(intptr_t)number , 0); + return 0; +} + +static int +_wmessage_int64(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + switch (lua_type(L,3)) { + case LUA_TSTRING : { + size_t len = 0; + const char * number = lua_tolstring(L,3,&len); + if (len !=8 ) { + return luaL_error(L,"Need an 8 length string for int64"); + } + const uint32_t * v = (const uint32_t *) number; + pbc_wmessage_integer(m, key, v[0] , v[1]); + break; + } + case LUA_TLIGHTUSERDATA : { + void * v = lua_touserdata(L,3); + uint64_t v64 = (uintptr_t)v; + pbc_wmessage_integer(m, key, (uint32_t)v64 , (uint32_t)(v64>>32)); + break; + } + default : + return luaL_error(L, "Need an int64 type"); + } + return 0; +} + +static int +_wmessage_int52(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + int64_t number = (int64_t)(luaL_checknumber(L,3)); + uint32_t hi = (uint32_t)(number >> 32); + pbc_wmessage_integer(m, key, (uint32_t)number, hi); + + return 0; +} + +static int +_wmessage_uint52(lua_State *L) { + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + const char * key = luaL_checkstring(L,2); + lua_Number v = (luaL_checknumber(L,3)); + if (v < 0) { + return luaL_error(L, "negative number : %f passed to unsigned field",v); + } + uint64_t number = (uint64_t)v; + uint32_t hi = (uint32_t)(number >> 32); + pbc_wmessage_integer(m, key, (uint32_t)number, hi); + + return 0; +} + +static int +_wmessage_buffer(lua_State *L) { + struct pbc_slice slice; + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + pbc_wmessage_buffer(m , &slice); + lua_pushlightuserdata(L, slice.buffer); + lua_pushinteger(L, slice.len); + return 2; +} + +static int +_wmessage_buffer_string(lua_State *L) { + struct pbc_slice slice; + struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1); + pbc_wmessage_buffer(m , &slice); + lua_pushlstring(L, (const char *)slice.buffer, slice.len); + return 1; +} + +/* + lightuserdata env + */ +static int +_last_error(lua_State *L) { + struct pbc_env * env = (struct pbc_env *)checkuserdata(L, 1); + const char * err = pbc_error(env); + lua_pushstring(L,err); + return 1; +} + +/* + lightuserdata env + string message + string format + */ +static int +_pattern_new(lua_State *L) { + struct pbc_env * env = (struct pbc_env *)checkuserdata(L, 1); + const char * message = luaL_checkstring(L,2); + const char * format = luaL_checkstring(L,3); + struct pbc_pattern * pat = pbc_pattern_new(env, message, format); + if (pat == NULL) { + return luaL_error(L, "create patten %s (%s) failed", message , format); + } + lua_pushlightuserdata(L,pat); + + return 1; +} + +static int +_pattern_delete(lua_State *L) { + struct pbc_pattern * pat = (struct pbc_pattern *)lua_touserdata(L,1); + pbc_pattern_delete(pat); + + return 0; +} + +static void * +_push_value(lua_State *L, char * ptr, char type) { + switch(type) { + case 'u': { + uint64_t v = *(uint64_t*)ptr; + ptr += 8; + lua_pushnumber(L,(lua_Number)v); + break; + } + case 'i': { + int32_t v = *(int32_t*)ptr; + ptr += 4; + lua_pushinteger(L,v); + break; + } + case 'b': { + int32_t v = *(int32_t*)ptr; + ptr += 4; + lua_pushboolean(L,v); + break; + } + case 'p': { + uint32_t v = *(uint32_t*)ptr; + ptr += 4; + lua_pushlightuserdata(L,(void *)(intptr_t)v); + break; + } + case 'x': { + lua_pushlstring(L,ptr,8); + ptr += 8; + break; + } + case 'd': { + int64_t v = *(int64_t*)ptr; + ptr += 8; + lua_pushnumber(L,(lua_Number)v); + break; + } + case 'r': { + double v = *(double *)ptr; + ptr += 8; + lua_pushnumber(L,v); + break; + } + case 's': { + struct pbc_slice * slice = (struct pbc_slice *)ptr; + lua_pushlstring(L,(const char *)slice->buffer, slice->len); + ptr += sizeof(struct pbc_slice); + break; + } + case 'm': { + struct pbc_slice * slice = (struct pbc_slice *)ptr; + lua_createtable(L,2,0); + lua_pushlightuserdata(L, slice->buffer); + lua_rawseti(L,-2,1); + lua_pushinteger(L,slice->len); + lua_rawseti(L,-2,2); + ptr += sizeof(struct pbc_slice); + break; + } + } + return ptr; +} + +static void +_push_array(lua_State *L, pbc_array array, char type, int index) { + switch (type) { + case 'I': { + int v = pbc_array_integer(array, index, NULL); + lua_pushinteger(L, v); + break; + } + case 'U': { + uint32_t hi = 0; + uint32_t low = pbc_array_integer(array, index, &hi); + uint64_t v = (uint64_t)hi << 32 | (uint64_t)low; + lua_pushnumber(L, (lua_Number)v); + break; + } + case 'D': { + uint32_t hi = 0; + uint32_t low = pbc_array_integer(array, index, &hi); + uint64_t v = (uint64_t)hi << 32 | (uint64_t)low; + lua_pushnumber(L, (lua_Number)((int64_t)v)); + break; + } + case 'B': { + int v = pbc_array_integer(array, index, NULL); + lua_pushboolean(L, v); + break; + } + case 'P': { + uint32_t v = pbc_array_integer(array, index, NULL); + lua_pushlightuserdata(L,(void *)(intptr_t)v); + break; + } + case 'X': { + uint32_t hi = 0; + uint32_t low = pbc_array_integer(array, index, &hi); + uint64_t v = (uint64_t)low | (uint64_t)hi << 32; + lua_pushlstring(L, (char *)&v, 8); + break; + } + case 'R': { + double v = pbc_array_real(array, index); + lua_pushnumber(L, v); + break; + } + case 'S': { + struct pbc_slice * slice = pbc_array_slice(array, index); + lua_pushlstring(L, (const char *)slice->buffer,slice->len); + break; + } + case 'M': { + struct pbc_slice * slice = pbc_array_slice(array, index); + lua_createtable(L,2,0); + lua_pushlightuserdata(L,slice->buffer); + lua_rawseti(L,-2,1); + lua_pushinteger(L,slice->len); + lua_rawseti(L,-2,2); + break; + } + } + lua_rawseti(L,-2,index+1); +} + +/* + lightuserdata pattern + string format "ixrsmb" + integer size + lightuserdata buffer + integer buffer_len + */ +static int +_pattern_unpack(lua_State *L) { + struct pbc_pattern * pat = (struct pbc_pattern *)checkuserdata(L, 1); + if (pat == NULL) { + return luaL_error(L, "unpack pattern is NULL"); + } + size_t format_sz = 0; + const char * format = lua_tolstring(L,2,&format_sz); + int size = lua_tointeger(L,3); + struct pbc_slice slice; + if (lua_isstring(L,4)) { + size_t buffer_len = 0; + const char *buffer = luaL_checklstring(L,4,&buffer_len); + slice.buffer = (void *)buffer; + slice.len = buffer_len; + } else { + if (!lua_isuserdata(L,4)) { + return luaL_error(L, "Need a userdata"); + } + slice.buffer = lua_touserdata(L,4); + slice.len = luaL_checkinteger(L,5); + } + + char * temp = (char *)alloca(size); + int ret = pbc_pattern_unpack(pat, &slice, temp); + if (ret < 0) { + return 0; + } + lua_checkstack(L, format_sz + 3); + int i; + char * ptr = temp; + bool array = false; + for (i=0;i= 'a' && type <='z') { + ptr = (char *)_push_value(L,ptr,type); + } else { + array = true; + int n = pbc_array_size((struct _pbc_array *)ptr); + lua_createtable(L,n,0); + int j; + for (j=0;jbuffer = (void*)str; + slice->len = sz; + return ptr + sizeof(struct pbc_slice); + } + case 'm': { + struct pbc_slice * slice = (struct pbc_slice *)ptr; + if (lua_istable(L,index)) { + lua_rawgeti(L,index,1); + slice->buffer = lua_touserdata(L,-1); + lua_rawgeti(L,index,2); + slice->len = lua_tointeger(L,-1); + lua_pop(L,2); + } else { + size_t sz = 0; + const char * buffer = luaL_checklstring(L, index, &sz); + slice->buffer = (void *)buffer; + slice->len = sz; + } + return ptr + sizeof(struct pbc_slice); + } + default: + luaL_error(L,"unknown format %c", type); + return ptr; + } +} + +static void +_get_array_value(lua_State *L, pbc_array array, char type) { + switch(type) { + case 'I': { + int32_t v = luaL_checkinteger(L, -1); + uint32_t hi = 0; + if (v<0) { + hi = ~0; + } + pbc_array_push_integer(array, v, hi); + break; + } + case 'U' : { + uint64_t v = (uint64_t)luaL_checknumber(L, -1); + pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32)); + break; + } + case 'D' : { + int64_t v = (int64_t)luaL_checknumber(L, -1); + pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32)); + break; + } + case 'B': { + int32_t v = lua_toboolean(L, -1); + pbc_array_push_integer(array, v ? 1: 0, 0); + break; + } + case 'P': { + void *p = lua_touserdata(L, -1); + uint32_t v = (uint32_t)(intptr_t)p; + pbc_array_push_integer(array, v, 0); + break; + } + case 'X': { + const char * i64 = luaL_checkstring(L, -1); + uint64_t v = *(uint64_t *)i64; + pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32)); + break; + } + case 'R': { + double v = luaL_checknumber(L, -1); + pbc_array_push_real(array, v); + break; + } + case 'S': { + size_t sz = 0; + const char * str = luaL_checklstring(L, -1, &sz); + struct pbc_slice slice; + slice.buffer = (void*)str; + slice.len = sz; + pbc_array_push_slice(array, &slice); + break; + } + case 'M': { + struct pbc_slice slice; + if (lua_istable(L,-1)) { + lua_rawgeti(L,-1,1); + slice.buffer = lua_touserdata(L,-1); + lua_rawgeti(L,-2,2); + slice.len = lua_tointeger(L,-1); + lua_pop(L,2); + } else { + size_t sz = 0; + const char * buffer = luaL_checklstring(L, -1, &sz); + slice.buffer = (void *)buffer; + slice.len = sz; + } + pbc_array_push_slice(array, &slice); + break; + } + } +} + +/* + lightuserdata pattern + string format "ixrsmbp" + integer size + */ +static int +_pattern_pack(lua_State *L) { + struct pbc_pattern * pat = (struct pbc_pattern *)checkuserdata(L,1); + if (pat == NULL) { + return luaL_error(L, "pack pattern is NULL"); + } + size_t format_sz = 0; + const char * format = lua_tolstring(L,2,&format_sz); + int size = lua_tointeger(L,3); + + char * data = (char *)alloca(size); +// A trick , we don't need default value. zero buffer for array and message field. +// pbc_pattern_set_default(pat, data); + memset(data, 0 , size); + + char * ptr = data; + + int i; + + for (i=0;i= 'a' && format[i] <='z') { + ptr = _get_value(L, 4+i, ptr, format[i]); + } else { + if (!lua_istable(L,4+i)) { + luaL_error(L,"need table for array type"); + } + int j; + int n = lua_rawlen(L,4+i); + for (j=0;ji.low); + break; + case PBC_REAL: + lua_pushnumber(L, v->f); + break; + case PBC_BOOL: + lua_pushboolean(L, v->i.low); + break; + case PBC_ENUM: + lua_pushstring(L, v->e.name); + break; + case PBC_BYTES: + case PBC_STRING: + lua_pushlstring(L, (const char *)v->s.buffer , v->s.len); + break; + case PBC_MESSAGE: + lua_pushvalue(L, -3); + lua_pushstring(L, type_name); + lua_pushlstring(L, (const char *)v->s.buffer , v->s.len); + lua_call(L, 2 , 1); + break; + case PBC_FIXED64: + lua_pushlstring(L, (const char *)&(v->i), 8); + break; + case PBC_FIXED32: + lua_pushlightuserdata(L,(void *)(intptr_t)v->i.low); + break; + case PBC_INT64: { + uint64_t v64 = (uint64_t)(v->i.hi) << 32 | (uint64_t)(v->i.low); + lua_pushnumber(L,(lua_Number)(int64_t)v64); + break; + } + case PBC_UINT: { + uint64_t v64 = (uint64_t)(v->i.hi) << 32 | (uint64_t)(v->i.low); + lua_pushnumber(L,(lua_Number)v64); + break; + } + default: + luaL_error(L, "Unknown type %s", type_name); + break; + } +} + +/* + -3: function decode + -2: table key + -1: table id + */ +static void +decode_cb(void *ud, int type, const char * type_name, union pbc_value *v, int id, const char *key) { + lua_State *L = (lua_State *)ud; + if (key == NULL) { + // undefined field + return; + } + if (type & PBC_REPEATED) { + push_value(L, type & ~PBC_REPEATED, type_name, v); + new_array(L, id , key); // func.decode table.key table.id value array + int n = lua_rawlen(L,-1); + lua_insert(L, -2); // func.decode table.key table.id array value + lua_rawseti(L, -2 , n+1); // func.decode table.key table.id array + lua_pop(L,1); + } else { + push_value(L, type, type_name, v); + lua_setfield(L, -3 , key); + } +} + +/* + :1 lightuserdata env + :2 function decode_message + :3 table target + :4 string type + :5 string data + :5 lightuserdata pointer + :6 integer len + + table + */ +static int +_decode(lua_State *L) { + struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1); + luaL_checktype(L, 2 , LUA_TFUNCTION); + luaL_checktype(L, 3 , LUA_TTABLE); + const char * type = luaL_checkstring(L,4); + struct pbc_slice slice; + if (lua_type(L,5) == LUA_TSTRING) { + size_t len; + slice.buffer = (void *)luaL_checklstring(L,5,&len); + slice.len = (int)len; + } else { + slice.buffer = checkuserdata(L,5); + slice.len = luaL_checkinteger(L,6); + } + lua_pushvalue(L, 2); + lua_pushvalue(L, 3); + lua_newtable(L); + + int n = pbc_decode(env, type, &slice, decode_cb, L); + if (n<0) { + lua_pushboolean(L,0); + } else { + lua_pushboolean(L,1); + } + return 1; +} + +struct gcobj { + struct pbc_env * env; + int size_pat; + int cap_pat; + struct pbc_pattern ** pat; + int size_msg; + int cap_msg; + struct pbc_rmessage ** msg; +}; + +static int +_clear_gcobj(lua_State *L) { + struct gcobj * obj = (struct gcobj *)lua_touserdata(L,1); + int i; + for (i=0;isize_pat;i++) { + pbc_pattern_delete(obj->pat[i]); + } + for (i=0;isize_msg;i++) { + pbc_rmessage_delete(obj->msg[i]); + } + free(obj->pat); + free(obj->msg); + obj->pat = NULL; + obj->msg = NULL; + pbc_delete(obj->env); + obj->env = NULL; + + return 0; +} + +static int +_gc(lua_State *L) { + struct gcobj * obj = (struct gcobj *)lua_newuserdata(L,sizeof(*obj)); + obj->env = (struct pbc_env *)lua_touserdata(L,1); + obj->size_pat = 0; + obj->cap_pat = 4; + obj->size_msg = 0; + obj->cap_msg = 4; + obj->pat = (struct pbc_pattern **)malloc(obj->cap_pat * sizeof(struct pbc_pattern *)); + obj->msg = (struct pbc_rmessage **)malloc(obj->cap_msg * sizeof(struct pbc_rmessage *)); + + lua_createtable(L,0,1); + lua_pushcfunction(L, _clear_gcobj); + lua_setfield(L,-2,"__gc"); + lua_setmetatable(L,-2); + + return 1; +} + +static int +_add_pattern(lua_State *L) { + struct gcobj * obj = (struct gcobj *)lua_touserdata(L,1); + if (obj->size_pat >= obj->cap_pat) { + obj->cap_pat *= 2; + obj->pat = (struct pbc_pattern **)realloc(obj->pat, obj->cap_pat * sizeof(struct pbc_pattern *)); + } + struct pbc_pattern * pat = (struct pbc_pattern *)lua_touserdata(L,2); + obj->pat[obj->size_pat++] = pat; + return 0; +} + +static int +_add_rmessage(lua_State *L) { + struct gcobj * obj = (struct gcobj *)lua_touserdata(L,1); + if (obj->size_msg >= obj->cap_msg) { + obj->cap_msg *= 2; + obj->msg = (struct pbc_rmessage **)realloc(obj->msg, obj->cap_msg * sizeof(struct pbc_rmessage *)); + } + struct pbc_rmessage * msg = (struct pbc_rmessage *)lua_touserdata(L,2); + obj->msg[obj->size_msg++] = msg; + return 0; +} + +int +luaopen_protobuf_c(lua_State *L) { + luaL_Reg reg[] = { + {"_env_new" , _env_new }, + {"_env_register" , _env_register }, + {"_env_type", _env_type }, + {"_rmessage_new" , _rmessage_new }, + {"_rmessage_delete" , _rmessage_delete }, + {"_rmessage_integer" , _rmessage_integer }, + {"_rmessage_int32", _rmessage_int32 }, + {"_rmessage_int64", _rmessage_int64 }, + {"_rmessage_int52", _rmessage_int52 }, + {"_rmessage_uint52", _rmessage_uint52 }, + {"_rmessage_real" , _rmessage_real }, + {"_rmessage_string" , _rmessage_string }, + {"_rmessage_message" , _rmessage_message }, + {"_rmessage_size" , _rmessage_size }, + {"_wmessage_new", _wmessage_new }, + {"_wmessage_delete", _wmessage_delete }, + {"_wmessage_integer", _wmessage_integer }, + {"_wmessage_real", _wmessage_real }, + {"_wmessage_string", _wmessage_string }, + {"_wmessage_message", _wmessage_message }, + {"_wmessage_int32", _wmessage_int32 }, + {"_wmessage_int64", _wmessage_int64 }, + {"_wmessage_int52", _wmessage_int52 }, + {"_wmessage_uint52", _wmessage_uint52 }, + {"_wmessage_buffer", _wmessage_buffer }, + {"_wmessage_buffer_string", _wmessage_buffer_string }, + {"_pattern_new", _pattern_new }, + {"_pattern_delete", _pattern_delete }, + {"_pattern_size", _pattern_size }, + {"_pattern_unpack", _pattern_unpack }, + {"_pattern_pack", _pattern_pack }, + {"_last_error", _last_error }, + {"_decode", _decode }, + {"_gc", _gc }, + {"_add_pattern", _add_pattern }, + {"_add_rmessage", _add_rmessage }, + {NULL,NULL}, + }; + + luaL_checkversion(L); + luaL_newlib(L, reg); + + return 1; +} diff --git a/luaclib/pb/binding/lua/protobuf.lua b/luaclib/pb/binding/lua/protobuf.lua new file mode 100644 index 0000000..956e990 --- /dev/null +++ b/luaclib/pb/binding/lua/protobuf.lua @@ -0,0 +1,558 @@ +local c = require "protobuf.c" + +local setmetatable = setmetatable +local type = type +local table = table +local assert = assert +local pairs = pairs +local ipairs = ipairs +local string = string +local print = print +local io = io +local tinsert = table.insert +local rawget = rawget + +--module "protobuf" +local Protobuf = {} + +local _pattern_cache = {} + +-- skynet clear +local P = c._env_new() +local GC = c._gc(P) + +function Protobuf.lasterror() + return c._last_error(P) +end + +local decode_type_cache = {} +local _R_meta = {} + +function _R_meta:__index(key) + local v = decode_type_cache[self._CType][key](self, key) + self[key] = v + return v +end + +local _reader = {} + +function _reader:int(key) + return c._rmessage_integer(self._CObj , key , 0) +end + +function _reader:real(key) + return c._rmessage_real(self._CObj , key , 0) +end + +function _reader:string(key) + return c._rmessage_string(self._CObj , key , 0) +end + +function _reader:bool(key) + return c._rmessage_integer(self._CObj , key , 0) ~= 0 +end + +function _reader:message(key, message_type) + local rmessage = c._rmessage_message(self._CObj , key , 0) + if rmessage then + local v = { + _CObj = rmessage, + _CType = message_type, + _Parent = self, + } + return setmetatable( v , _R_meta ) + end +end + +function _reader:int32(key) + return c._rmessage_int32(self._CObj , key , 0) +end + +function _reader:int64(key) + return c._rmessage_int64(self._CObj , key , 0) +end + +function _reader:int52(key) + return c._rmessage_int52(self._CObj , key , 0) +end + +function _reader:uint52(key) + return c._rmessage_uint52(self._CObj , key , 0) +end + +function _reader:int_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_integer(cobj , key , i)) + end + return ret +end + +function _reader:real_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_real(cobj , key , i)) + end + return ret +end + +function _reader:string_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_string(cobj , key , i)) + end + return ret +end + +function _reader:bool_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_integer(cobj , key , i) ~= 0) + end + return ret +end + +function _reader:message_repeated(key, message_type) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + local m = { + _CObj = c._rmessage_message(cobj , key , i), + _CType = message_type, + _Parent = self, + } + tinsert(ret, setmetatable( m , _R_meta )) + end + return ret +end + +function _reader:int32_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_int32(cobj , key , i)) + end + return ret +end + +function _reader:int64_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_int64(cobj , key , i)) + end + return ret +end + +function _reader:int52_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_int52(cobj , key , i)) + end + return ret +end + +function _reader:uint52_repeated(key) + local cobj = self._CObj + local n = c._rmessage_size(cobj , key) + local ret = {} + for i=0,n-1 do + tinsert(ret, c._rmessage_uint52(cobj , key , i)) + end + return ret +end + +_reader[1] = function(msg) return _reader.int end +_reader[2] = function(msg) return _reader.real end +_reader[3] = function(msg) return _reader.bool end +_reader[4] = function(msg) return _reader.string end +_reader[5] = function(msg) return _reader.string end +_reader[6] = function(msg) + local message = _reader.message + return function(self,key) + return message(self, key, msg) + end +end +_reader[7] = function(msg) return _reader.int64 end +_reader[8] = function(msg) return _reader.int32 end +_reader[9] = _reader[5] +_reader[10] = function(msg) return _reader.int52 end +_reader[11] = function(msg) return _reader.uint52 end + +_reader[128+1] = function(msg) return _reader.int_repeated end +_reader[128+2] = function(msg) return _reader.real_repeated end +_reader[128+3] = function(msg) return _reader.bool_repeated end +_reader[128+4] = function(msg) return _reader.string_repeated end +_reader[128+5] = function(msg) return _reader.string_repeated end +_reader[128+6] = function(msg) + local message = _reader.message_repeated + return function(self,key) + return message(self, key, msg) + end +end +_reader[128+7] = function(msg) return _reader.int64_repeated end +_reader[128+8] = function(msg) return _reader.int32_repeated end +_reader[128+9] = _reader[128+5] +_reader[128+10] = function(msg) return _reader.int52_repeated end +_reader[128+11] = function(msg) return _reader.uint52_repeated end + +local _decode_type_meta = {} + +function _decode_type_meta:__index(key) + local t, msg = c._env_type(P, self._CType, key) + local func = assert(_reader[t],key)(msg) + self[key] = func + return func +end + +setmetatable(decode_type_cache , { + __index = function(self, key) + local v = setmetatable({ _CType = key } , _decode_type_meta) + self[key] = v + return v + end +}) + +local function decode_message( message , buffer, length) + local rmessage = c._rmessage_new(P, message, buffer, length) + if rmessage then + local self = { + _CObj = rmessage, + _CType = message, + } + c._add_rmessage(GC,rmessage) + return setmetatable( self , _R_meta ) + end +end + +----------- encode ---------------- + +local encode_type_cache = {} + +local function encode_message(CObj, message_type, t) + local type = encode_type_cache[message_type] + for k,v in pairs(t) do + local func = type[k] + func(CObj, k , v) + end +end + +local _writer = { + int = c._wmessage_integer, + real = c._wmessage_real, + enum = c._wmessage_string, + string = c._wmessage_string, + int64 = c._wmessage_int64, + int32 = c._wmessage_int32, + int52 = c._wmessage_int52, + uint52 = c._wmessage_uint52, +} + +function _writer:bool(k,v) + c._wmessage_integer(self, k, v and 1 or 0) +end + +function _writer:message(k, v , message_type) + local submessage = c._wmessage_message(self, k) + encode_message(submessage, message_type, v) +end + +function _writer:int_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_integer(self,k,v) + end +end + +function _writer:real_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_real(self,k,v) + end +end + +function _writer:bool_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_integer(self, k, v and 1 or 0) + end +end + +function _writer:string_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_string(self,k,v) + end +end + +function _writer:message_repeated(k,v, message_type) + for _,v in ipairs(v) do + local submessage = c._wmessage_message(self, k) + encode_message(submessage, message_type, v) + end +end + +function _writer:int32_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_int32(self,k,v) + end +end + +function _writer:int64_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_int64(self,k,v) + end +end + +function _writer:int52_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_int52(self,k,v) + end +end + +function _writer:uint52_repeated(k,v) + for _,v in ipairs(v) do + c._wmessage_uint52(self,k,v) + end +end + +_writer[1] = function(msg) return _writer.int end +_writer[2] = function(msg) return _writer.real end +_writer[3] = function(msg) return _writer.bool end +_writer[4] = function(msg) return _writer.string end +_writer[5] = function(msg) return _writer.string end +_writer[6] = function(msg) + local message = _writer.message + return function(self,key , v) + return message(self, key, v, msg) + end +end +_writer[7] = function(msg) return _writer.int64 end +_writer[8] = function(msg) return _writer.int32 end +_writer[9] = _writer[5] +_writer[10] = function(msg) return _writer.int52 end +_writer[11] = function(msg) return _writer.uint52 end + +_writer[128+1] = function(msg) return _writer.int_repeated end +_writer[128+2] = function(msg) return _writer.real_repeated end +_writer[128+3] = function(msg) return _writer.bool_repeated end +_writer[128+4] = function(msg) return _writer.string_repeated end +_writer[128+5] = function(msg) return _writer.string_repeated end +_writer[128+6] = function(msg) + local message = _writer.message_repeated + return function(self,key, v) + return message(self, key, v, msg) + end +end +_writer[128+7] = function(msg) return _writer.int64_repeated end +_writer[128+8] = function(msg) return _writer.int32_repeated end +_writer[128+9] = _writer[128+5] +_writer[128+10] = function(msg) return _writer.int52_repeated end +_writer[128+11] = function(msg) return _writer.uint52_repeated end + +local _encode_type_meta = {} + +function _encode_type_meta:__index(key) + local t, msg = c._env_type(P, self._CType, key) + local func = assert(_writer[t],key)(msg) + self[key] = func + return func +end + +setmetatable(encode_type_cache , { + __index = function(self, key) + local v = setmetatable({ _CType = key } , _encode_type_meta) + self[key] = v + return v + end +}) + +function Protobuf.encode( message, t , func , ...) + local encoder = c._wmessage_new(P, message) + assert(encoder , message) + encode_message(encoder, message, t) + if func then + local buffer, len = c._wmessage_buffer(encoder) + local ret = func(buffer, len, ...) + c._wmessage_delete(encoder) + return ret + else + local s = c._wmessage_buffer_string(encoder) + c._wmessage_delete(encoder) + return s + end +end + +--------- unpack ---------- + +local _pattern_type = { + [1] = {"%d","i"}, + [2] = {"%F","r"}, + [3] = {"%d","b"}, + [4] = {"%d","i"}, + [5] = {"%s","s"}, + [6] = {"%s","m"}, + [7] = {"%D","x"}, + [8] = {"%d","p"}, + [10] = {"%D","d"}, + [11] = {"%D","u"}, + [128+1] = {"%a","I"}, + [128+2] = {"%a","R"}, + [128+3] = {"%a","B"}, + [128+4] = {"%a","I"}, + [128+5] = {"%a","S"}, + [128+6] = {"%a","M"}, + [128+7] = {"%a","X"}, + [128+8] = {"%a","P"}, + [128+10] = {"%a", "D" }, + [128+11] = {"%a", "U" }, +} + +_pattern_type[9] = _pattern_type[5] +_pattern_type[128+9] = _pattern_type[128+5] + + +local function _pattern_create(pattern) + local iter = string.gmatch(pattern,"[^ ]+") + local message = iter() + local cpat = {} + local lua = {} + for v in iter do + local tidx = c._env_type(P, message, v) + local t = _pattern_type[tidx] + assert(t,tidx) + tinsert(cpat,v .. " " .. t[1]) + tinsert(lua,t[2]) + end + local cobj = c._pattern_new(P, message , "@" .. table.concat(cpat," ")) + if cobj == nil then + return + end + c._add_pattern(GC, cobj) + local pat = { + CObj = cobj, + format = table.concat(lua), + size = 0 + } + pat.size = c._pattern_size(pat.format) + + return pat +end + +setmetatable(_pattern_cache, { + __index = function(t, key) + local v = _pattern_create(key) + t[key] = v + return v + end +}) + +function Protobuf.unpack(pattern, buffer, length) + local pat = _pattern_cache[pattern] + return c._pattern_unpack(pat.CObj , pat.format, pat.size, buffer, length) +end + +function Protobuf.pack(pattern, ...) + local pat = _pattern_cache[pattern] + return c._pattern_pack(pat.CObj, pat.format, pat.size , ...) +end + +function Protobuf.check(typename , field) + if field == nil then + return c._env_type(P,typename) + else + return c._env_type(P,typename,field) ~=0 + end +end + +-------------- + +local default_cache = {} + +-- todo : clear default_cache, v._CObj + +local function default_table(typename) + local v = default_cache[typename] + if v then + return v + end + + v = { __index = assert(decode_message(typename , "")) } + + default_cache[typename] = v + return v +end + +local decode_message_mt = {} + +local function decode_message_cb(typename, buffer) + return setmetatable ( { typename, buffer } , decode_message_mt) +end + +function Protobuf.decode(typename, buffer, length) + local ret = {} + local ok = c._decode(P, decode_message_cb , ret , typename, buffer, length) + if ok then + return setmetatable(ret , default_table(typename)) + else + return false , c._last_error(P) + end +end + +local function expand(tbl) + local typename = rawget(tbl , 1) + local buffer = rawget(tbl , 2) + tbl[1] , tbl[2] = nil , nil + assert(c._decode(P, decode_message_cb , tbl , typename, buffer), typename) + setmetatable(tbl , default_table(typename)) +end + +function decode_message_mt.__index(tbl, key) + expand(tbl) + return tbl[key] +end + +function decode_message_mt.__pairs(tbl) + expand(tbl) + return pairs(tbl) +end + +local function set_default(typename, tbl) + for k,v in pairs(tbl) do + if type(v) == "table" then + local t, msg = c._env_type(P, typename, k) + if t == 6 then + set_default(msg, v) + elseif t == 128+6 then + for _,v in ipairs(v) do + set_default(msg, v) + end + end + end + end + return setmetatable(tbl , default_table(typename)) +end + +function Protobuf.register( buffer) + c._env_register(P, buffer) +end + +function Protobuf.register_file(filename) + local f = assert(io.open(filename , "rb")) + local buffer = f:read "*a" + c._env_register(P, buffer) + f:close() +end + +Protobuf.default=set_default + +return Protobuf diff --git a/luaclib/pb/binding/lua/test.lua b/luaclib/pb/binding/lua/test.lua new file mode 100644 index 0000000..d96a355 --- /dev/null +++ b/luaclib/pb/binding/lua/test.lua @@ -0,0 +1,46 @@ +require "protobuf" + +addr = io.open("../../build/addressbook.pb","rb") +buffer = addr:read "*a" +addr:close() + +protobuf.register(buffer) + +t = protobuf.decode("google.protobuf.FileDescriptorSet", buffer) + +proto = t.file[1] + +print(proto.name) +print(proto.package) + +message = proto.message_type + +for _,v in ipairs(message) do + print(v.name) + for _,v in ipairs(v.field) do + print("\t".. v.name .. " ["..v.number.."] " .. v.label) + end +end + +addressbook = { + name = "Alice", + id = 12345, + phone = { + { number = "1301234567" }, + { number = "87654321", type = "WORK" }, + } +} + +code = protobuf.encode("tutorial.Person", addressbook) + +decode = protobuf.decode("tutorial.Person" , code) + +print(decode.name) +print(decode.id) +for _,v in ipairs(decode.phone) do + print("\t"..v.number, v.type) +end + +phonebuf = protobuf.pack("tutorial.Person.PhoneNumber number","87654321") +buffer = protobuf.pack("tutorial.Person name id phone", "Alice", 123, { phonebuf }) +print(protobuf.unpack("tutorial.Person name id phone", buffer)) diff --git a/luaclib/pb/binding/lua/test2.lua b/luaclib/pb/binding/lua/test2.lua new file mode 100644 index 0000000..c704c06 --- /dev/null +++ b/luaclib/pb/binding/lua/test2.lua @@ -0,0 +1,32 @@ +local protobuf = require "protobuf" + +addr = io.open("../../build/addressbook.pb","rb") +buffer = addr:read "*a" +addr:close() +protobuf.register(buffer) + +local person = { + name = "Alice", + id = 123, + phone = { + { number = "123456789" , type = "MOBILE" }, + { number = "87654321" , type = "HOME" }, + } +} + +local buffer = protobuf.encode("tutorial.Person", person) + +local t = protobuf.decode("tutorial.Person", buffer) + +for k,v in pairs(t) do + if type(k) == "string" then + print(k,v) + end +end + +print(t.phone[2].type) + +for k,v in pairs(t.phone[1]) do + print(k,v) +end + diff --git a/luaclib/pb/binding/lua/testparser.lua b/luaclib/pb/binding/lua/testparser.lua new file mode 100644 index 0000000..98901b6 --- /dev/null +++ b/luaclib/pb/binding/lua/testparser.lua @@ -0,0 +1,26 @@ +protobuf = require "protobuf" +parser = require "parser" + +t = parser.register("addressbook.proto","../../test") + +addressbook = { + name = "Alice", + id = 12345, + phone = { + { number = "1301234567" }, + { number = "87654321", type = "WORK" }, + } +} + +code = protobuf.encode("tutorial.Person", addressbook) + +decode = protobuf.decode("tutorial.Person" , code) + +print(decode.name) +print(decode.id) +for _,v in ipairs(decode.phone) do + print("\t"..v.number, v.type) +end + +buffer = protobuf.pack("tutorial.Person name id", "Alice", 123) +print(protobuf.unpack("tutorial.Person name id", buffer)) \ No newline at end of file diff --git a/luaclib/pb/license.txt b/luaclib/pb/license.txt new file mode 100644 index 0000000..33e81c1 --- /dev/null +++ b/luaclib/pb/license.txt @@ -0,0 +1,19 @@ +Copyright (C) 2011 by Cloud Wu + +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. \ No newline at end of file diff --git a/luaclib/pb/pb.vcxproj.filters b/luaclib/pb/pb.vcxproj.filters new file mode 100644 index 0000000..0fa14e8 --- /dev/null +++ b/luaclib/pb/pb.vcxproj.filters @@ -0,0 +1,87 @@ + + + + + {5ebe0648-0543-4f41-97a2-f6a1b350c869} + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + \ No newline at end of file diff --git a/luaclib/pb/pb.vcxproj.user b/luaclib/pb/pb.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/luaclib/pb/pb.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/luaclib/pb/pbc.sdf b/luaclib/pb/pbc.sdf new file mode 100644 index 0000000..e90cb30 Binary files /dev/null and b/luaclib/pb/pbc.sdf differ diff --git a/luaclib/pb/pbc.sln b/luaclib/pb/pbc.sln new file mode 100644 index 0000000..ed6f4af --- /dev/null +++ b/luaclib/pb/pbc.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pbc", "pbc.vcxproj", "{82356F33-956B-4931-9977-BD7994B1C761}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {82356F33-956B-4931-9977-BD7994B1C761}.Debug|Win32.ActiveCfg = Debug|Win32 + {82356F33-956B-4931-9977-BD7994B1C761}.Debug|Win32.Build.0 = Debug|Win32 + {82356F33-956B-4931-9977-BD7994B1C761}.Release|Win32.ActiveCfg = Release|Win32 + {82356F33-956B-4931-9977-BD7994B1C761}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/luaclib/pb/pbc.v12.suo b/luaclib/pb/pbc.v12.suo new file mode 100644 index 0000000..31f8366 Binary files /dev/null and b/luaclib/pb/pbc.v12.suo differ diff --git a/luaclib/pb/pbc.vcxproj b/luaclib/pb/pbc.vcxproj new file mode 100644 index 0000000..6ffa79b --- /dev/null +++ b/luaclib/pb/pbc.vcxproj @@ -0,0 +1,111 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {EB20378F-8EE9-4DF1-9EE7-87BFB769F2FE} + pb + protobuf + + + + DynamicLibrary + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(ProjectDir) + $(ProjectName) + + + + Level3 + Disabled + false + ..\..\deps\lua;.\src + WIN32;_DEBUG;_WINDOWS;_USRDLL;LUA_CORE;LUA_BUILD_AS_DLL;LUA_COMPAT_5_2;LUA_COMPAT_5_1;%(PreprocessorDefinitions) + /D"inline"=__inline /D"bool"=char /D"true"=(1) /D"false"=(0) %(AdditionalOptions) + + + true + ..\..\deps\lua; + nlua.lib + protobuf.def + + + copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir) + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/luaclib/pb/pbc.vcxproj.filters b/luaclib/pb/pbc.vcxproj.filters new file mode 100644 index 0000000..0f79423 --- /dev/null +++ b/luaclib/pb/pbc.vcxproj.filters @@ -0,0 +1,87 @@ + + + + + {aa230bd6-7da2-4cfb-af39-52310e429716} + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + \ No newline at end of file diff --git a/luaclib/pb/pbc.vcxproj.user b/luaclib/pb/pbc.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/luaclib/pb/pbc.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/luaclib/pb/pbc.xcodeproj/project.pbxproj b/luaclib/pb/pbc.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e55813b --- /dev/null +++ b/luaclib/pb/pbc.xcodeproj/project.pbxproj @@ -0,0 +1,338 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + D7EF1338179E5530002B6A46 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7EF1337179E5530002B6A46 /* Foundation.framework */; }; + D7EF135D179E5626002B6A46 /* alloc.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1346179E5626002B6A46 /* alloc.c */; }; + D7EF135E179E5626002B6A46 /* array.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1348179E5626002B6A46 /* array.c */; }; + D7EF135F179E5626002B6A46 /* bootstrap.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF134A179E5626002B6A46 /* bootstrap.c */; }; + D7EF1360179E5626002B6A46 /* context.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF134C179E5626002B6A46 /* context.c */; }; + D7EF1361179E5626002B6A46 /* decode.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF134E179E5626002B6A46 /* decode.c */; }; + D7EF1362179E5626002B6A46 /* map.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1350179E5626002B6A46 /* map.c */; }; + D7EF1363179E5626002B6A46 /* pattern.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1352179E5626002B6A46 /* pattern.c */; }; + D7EF1364179E5626002B6A46 /* proto.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1354179E5626002B6A46 /* proto.c */; }; + D7EF1365179E5626002B6A46 /* register.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1356179E5626002B6A46 /* register.c */; }; + D7EF1366179E5626002B6A46 /* rmessage.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1357179E5626002B6A46 /* rmessage.c */; }; + D7EF1367179E5626002B6A46 /* stringpool.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF1358179E5626002B6A46 /* stringpool.c */; }; + D7EF1368179E5626002B6A46 /* varint.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF135A179E5626002B6A46 /* varint.c */; }; + D7EF1369179E5626002B6A46 /* wmessage.c in Sources */ = {isa = PBXBuildFile; fileRef = D7EF135C179E5626002B6A46 /* wmessage.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + D7EF1332179E5530002B6A46 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/${PRODUCT_NAME}"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + D7EF1334179E5530002B6A46 /* libpbc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libpbc.a; sourceTree = BUILT_PRODUCTS_DIR; }; + D7EF1337179E5530002B6A46 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + D7EF133B179E5530002B6A46 /* pbc-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "pbc-Prefix.pch"; sourceTree = ""; }; + D7EF1346179E5626002B6A46 /* alloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = alloc.c; sourceTree = ""; }; + D7EF1347179E5626002B6A46 /* alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = alloc.h; sourceTree = ""; }; + D7EF1348179E5626002B6A46 /* array.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = array.c; sourceTree = ""; }; + D7EF1349179E5626002B6A46 /* array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = array.h; sourceTree = ""; }; + D7EF134A179E5626002B6A46 /* bootstrap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bootstrap.c; sourceTree = ""; }; + D7EF134B179E5626002B6A46 /* bootstrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bootstrap.h; sourceTree = ""; }; + D7EF134C179E5626002B6A46 /* context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = context.c; sourceTree = ""; }; + D7EF134D179E5626002B6A46 /* context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = context.h; sourceTree = ""; }; + D7EF134E179E5626002B6A46 /* decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decode.c; sourceTree = ""; }; + D7EF134F179E5626002B6A46 /* descriptor.pbc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = descriptor.pbc.h; sourceTree = ""; }; + D7EF1350179E5626002B6A46 /* map.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = map.c; sourceTree = ""; }; + D7EF1351179E5626002B6A46 /* map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = map.h; sourceTree = ""; }; + D7EF1352179E5626002B6A46 /* pattern.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pattern.c; sourceTree = ""; }; + D7EF1353179E5626002B6A46 /* pattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pattern.h; sourceTree = ""; }; + D7EF1354179E5626002B6A46 /* proto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = proto.c; sourceTree = ""; }; + D7EF1355179E5626002B6A46 /* proto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = proto.h; sourceTree = ""; }; + D7EF1356179E5626002B6A46 /* register.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = register.c; sourceTree = ""; }; + D7EF1357179E5626002B6A46 /* rmessage.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rmessage.c; sourceTree = ""; }; + D7EF1358179E5626002B6A46 /* stringpool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stringpool.c; sourceTree = ""; }; + D7EF1359179E5626002B6A46 /* stringpool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stringpool.h; sourceTree = ""; }; + D7EF135A179E5626002B6A46 /* varint.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = varint.c; sourceTree = ""; }; + D7EF135B179E5626002B6A46 /* varint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = varint.h; sourceTree = ""; }; + D7EF135C179E5626002B6A46 /* wmessage.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wmessage.c; sourceTree = ""; }; + D7EF136D179E5837002B6A46 /* pbc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pbc.h; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D7EF1331179E5530002B6A46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D7EF1338179E5530002B6A46 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D7EF132B179E5530002B6A46 = { + isa = PBXGroup; + children = ( + D7EF1339179E5530002B6A46 /* pbc */, + D7EF1336179E5530002B6A46 /* Frameworks */, + D7EF1335179E5530002B6A46 /* Products */, + ); + sourceTree = ""; + }; + D7EF1335179E5530002B6A46 /* Products */ = { + isa = PBXGroup; + children = ( + D7EF1334179E5530002B6A46 /* libpbc.a */, + ); + name = Products; + sourceTree = ""; + }; + D7EF1336179E5530002B6A46 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D7EF1337179E5530002B6A46 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D7EF1339179E5530002B6A46 /* pbc */ = { + isa = PBXGroup; + children = ( + D7EF136D179E5837002B6A46 /* pbc.h */, + D7EF1345179E5626002B6A46 /* src */, + D7EF133A179E5530002B6A46 /* Supporting Files */, + ); + path = pbc; + sourceTree = ""; + }; + D7EF133A179E5530002B6A46 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D7EF133B179E5530002B6A46 /* pbc-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D7EF1345179E5626002B6A46 /* src */ = { + isa = PBXGroup; + children = ( + D7EF1346179E5626002B6A46 /* alloc.c */, + D7EF1347179E5626002B6A46 /* alloc.h */, + D7EF1348179E5626002B6A46 /* array.c */, + D7EF1349179E5626002B6A46 /* array.h */, + D7EF134A179E5626002B6A46 /* bootstrap.c */, + D7EF134B179E5626002B6A46 /* bootstrap.h */, + D7EF134C179E5626002B6A46 /* context.c */, + D7EF134D179E5626002B6A46 /* context.h */, + D7EF134E179E5626002B6A46 /* decode.c */, + D7EF134F179E5626002B6A46 /* descriptor.pbc.h */, + D7EF1350179E5626002B6A46 /* map.c */, + D7EF1351179E5626002B6A46 /* map.h */, + D7EF1352179E5626002B6A46 /* pattern.c */, + D7EF1353179E5626002B6A46 /* pattern.h */, + D7EF1354179E5626002B6A46 /* proto.c */, + D7EF1355179E5626002B6A46 /* proto.h */, + D7EF1356179E5626002B6A46 /* register.c */, + D7EF1357179E5626002B6A46 /* rmessage.c */, + D7EF1358179E5626002B6A46 /* stringpool.c */, + D7EF1359179E5626002B6A46 /* stringpool.h */, + D7EF135A179E5626002B6A46 /* varint.c */, + D7EF135B179E5626002B6A46 /* varint.h */, + D7EF135C179E5626002B6A46 /* wmessage.c */, + ); + path = src; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + D7EF1333179E5530002B6A46 /* pbc */ = { + isa = PBXNativeTarget; + buildConfigurationList = D7EF1342179E5530002B6A46 /* Build configuration list for PBXNativeTarget "pbc" */; + buildPhases = ( + D7EF1330179E5530002B6A46 /* Sources */, + D7EF1331179E5530002B6A46 /* Frameworks */, + D7EF1332179E5530002B6A46 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = pbc; + productName = pbc; + productReference = D7EF1334179E5530002B6A46 /* libpbc.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D7EF132C179E5530002B6A46 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0460; + ORGANIZATIONNAME = ztgame; + }; + buildConfigurationList = D7EF132F179E5530002B6A46 /* Build configuration list for PBXProject "pbc" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = D7EF132B179E5530002B6A46; + productRefGroup = D7EF1335179E5530002B6A46 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D7EF1333179E5530002B6A46 /* pbc */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + D7EF1330179E5530002B6A46 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D7EF135D179E5626002B6A46 /* alloc.c in Sources */, + D7EF135E179E5626002B6A46 /* array.c in Sources */, + D7EF135F179E5626002B6A46 /* bootstrap.c in Sources */, + D7EF1360179E5626002B6A46 /* context.c in Sources */, + D7EF1361179E5626002B6A46 /* decode.c in Sources */, + D7EF1362179E5626002B6A46 /* map.c in Sources */, + D7EF1363179E5626002B6A46 /* pattern.c in Sources */, + D7EF1364179E5626002B6A46 /* proto.c in Sources */, + D7EF1365179E5626002B6A46 /* register.c in Sources */, + D7EF1366179E5626002B6A46 /* rmessage.c in Sources */, + D7EF1367179E5626002B6A46 /* stringpool.c in Sources */, + D7EF1368179E5626002B6A46 /* varint.c in Sources */, + D7EF1369179E5626002B6A46 /* wmessage.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D7EF1340179E5530002B6A46 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; + CLANG_CXX_LIBRARY = "compiler-default"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "“$(SRCROOT)/src\" “$(SRCROOT)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VALID_ARCHS = "armv7 armv7s i386"; + }; + name = Debug; + }; + D7EF1341179E5530002B6A46 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; + CLANG_CXX_LIBRARY = "compiler-default"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "“$(SRCROOT)/src\" “$(SRCROOT)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = "armv7 armv7s i386"; + }; + name = Release; + }; + D7EF1343179E5530002B6A46 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + DSTROOT = /tmp/pbc.dst; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "pbc/pbc-Prefix.pch"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + VALID_ARCHS = "armv7 armv6 i386"; + }; + name = Debug; + }; + D7EF1344179E5530002B6A46 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + DSTROOT = /tmp/pbc.dst; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "pbc/pbc-Prefix.pch"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + VALID_ARCHS = "armv7 armv6 i386"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D7EF132F179E5530002B6A46 /* Build configuration list for PBXProject "pbc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D7EF1340179E5530002B6A46 /* Debug */, + D7EF1341179E5530002B6A46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D7EF1342179E5530002B6A46 /* Build configuration list for PBXNativeTarget "pbc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D7EF1343179E5530002B6A46 /* Debug */, + D7EF1344179E5530002B6A46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D7EF132C179E5530002B6A46 /* Project object */; +} diff --git a/luaclib/pb/pbc.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/luaclib/pb/pbc.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b1f616f --- /dev/null +++ b/luaclib/pb/pbc.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/luaclib/pb/pbc.xcodeproj/project.xcworkspace/xcuserdata/dev.xcuserdatad/UserInterfaceState.xcuserstate b/luaclib/pb/pbc.xcodeproj/project.xcworkspace/xcuserdata/dev.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..a3b6a26 Binary files /dev/null and b/luaclib/pb/pbc.xcodeproj/project.xcworkspace/xcuserdata/dev.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/luaclib/pb/pbc.xcodeproj/project.xcworkspace/xcuserdata/juren.xcuserdatad/UserInterfaceState.xcuserstate b/luaclib/pb/pbc.xcodeproj/project.xcworkspace/xcuserdata/juren.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..1c9ffd7 Binary files /dev/null and b/luaclib/pb/pbc.xcodeproj/project.xcworkspace/xcuserdata/juren.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/luaclib/pb/pbc.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/pbc.xcscheme b/luaclib/pb/pbc.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/pbc.xcscheme new file mode 100644 index 0000000..a33c2c9 --- /dev/null +++ b/luaclib/pb/pbc.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/pbc.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/luaclib/pb/pbc.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/xcschememanagement.plist b/luaclib/pb/pbc.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..81bb0cb --- /dev/null +++ b/luaclib/pb/pbc.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + pbc.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + D7EF1333179E5530002B6A46 + + primary + + + + + diff --git a/luaclib/pb/pbc.xcodeproj/xcuserdata/juren.xcuserdatad/xcschemes/pbc.xcscheme b/luaclib/pb/pbc.xcodeproj/xcuserdata/juren.xcuserdatad/xcschemes/pbc.xcscheme new file mode 100644 index 0000000..a33c2c9 --- /dev/null +++ b/luaclib/pb/pbc.xcodeproj/xcuserdata/juren.xcuserdatad/xcschemes/pbc.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/luaclib/pb/pbc.xcodeproj/xcuserdata/juren.xcuserdatad/xcschemes/xcschememanagement.plist b/luaclib/pb/pbc.xcodeproj/xcuserdata/juren.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..81bb0cb --- /dev/null +++ b/luaclib/pb/pbc.xcodeproj/xcuserdata/juren.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + pbc.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + D7EF1333179E5530002B6A46 + + primary + + + + + diff --git a/luaclib/pb/pbc/pbc-Prefix.pch b/luaclib/pb/pbc/pbc-Prefix.pch new file mode 100644 index 0000000..a3c82d0 --- /dev/null +++ b/luaclib/pb/pbc/pbc-Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'pbc' target in the 'pbc' project +// + +#ifdef __OBJC__ + #import + #import "pbc.h" +#endif diff --git a/luaclib/pb/protobuf.def b/luaclib/pb/protobuf.def new file mode 100644 index 0000000..ec1d04a --- /dev/null +++ b/luaclib/pb/protobuf.def @@ -0,0 +1,3 @@ +LIBRARY + EXPORTS + luaopen_protobuf_c \ No newline at end of file diff --git a/luaclib/pb/src/alloc.c b/luaclib/pb/src/alloc.c new file mode 100644 index 0000000..96590ed --- /dev/null +++ b/luaclib/pb/src/alloc.c @@ -0,0 +1,84 @@ +#include +#include + +static int _g = 0; + +void * _pbcM_malloc(size_t sz) { + ++ _g; + return malloc(sz); +} + +void _pbcM_free(void *p) { + if (p) { + -- _g; + free(p); + } +} + +void* _pbcM_realloc(void *p, size_t sz) { + return realloc(p,sz); +} + +void _pbcM_memory() { + printf("%d\n",_g); +} + +struct heap_page { + struct heap_page * next; +}; + +struct heap { + struct heap_page *current; + int size; + int used; +}; + +struct heap * +_pbcH_new(int pagesize) { + int cap = 1024; + while(cap < pagesize) { + cap *= 2; + } + struct heap * h = (struct heap *)_pbcM_malloc(sizeof(struct heap)); + h->current = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + cap); + h->size = cap; + h->used = 0; + h->current->next = NULL; + return h; +} + +void +_pbcH_delete(struct heap *h) { + struct heap_page * p = h->current; + struct heap_page * next = p->next; + for(;;) { + _pbcM_free(p); + if (next == NULL) + break; + p = next; + next = p->next; + } + _pbcM_free(h); +} + +void* +_pbcH_alloc(struct heap *h, int size) { + size = (size + 3) & ~3; + if (h->size - h->used < size) { + struct heap_page * p; + if (size < h->size) { + p = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + h->size); + } else { + p = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + size); + } + p->next = h->current; + h->current = p; + h->used = size; + return (p+1); + } else { + char * buffer = (char *)(h->current + 1); + buffer += h->used; + h->used += size; + return buffer; + } +} diff --git a/luaclib/pb/src/alloc.h b/luaclib/pb/src/alloc.h new file mode 100644 index 0000000..7b611dd --- /dev/null +++ b/luaclib/pb/src/alloc.h @@ -0,0 +1,37 @@ +#ifndef PROTOBUF_C_ALLOC_H +#define PROTOBUF_C_ALLOC_H + +#include +#include + +void * _pbcM_malloc(size_t sz); +void _pbcM_free(void *p); +void * _pbcM_realloc(void *p, size_t sz); +void _pbcM_memory(); + +struct heap; + +struct heap * _pbcH_new(int pagesize); +void _pbcH_delete(struct heap *); +void* _pbcH_alloc(struct heap *, int size); + +#define HMALLOC(size) ((h) ? _pbcH_alloc(h, size) : _pbcM_malloc(size)) + +#define malloc _pbcM_malloc +#define free _pbcM_free +#define realloc _pbcM_realloc +#define memory _pbcM_memory + +#ifdef _WIN32 + +#include + +#endif + +#ifdef _MSC_VER + +#define alloca _alloca + +#endif + +#endif diff --git a/luaclib/pb/src/array.c b/luaclib/pb/src/array.c new file mode 100644 index 0000000..5ae4b37 --- /dev/null +++ b/luaclib/pb/src/array.c @@ -0,0 +1,139 @@ +#include "pbc.h" +#include "array.h" +#include "alloc.h" + +#include +#include + +struct array { + int number; + struct heap *heap; + union _pbc_var * a; +}; + +#define INNER_FIELD ((PBC_ARRAY_CAP - sizeof(struct array)) / sizeof(pbc_var)) + +void +_pbcA_open(pbc_array _array) { + struct array * a = (struct array *)_array; + a->number = 0; + a->heap = NULL; + a->a = (union _pbc_var *)(a+1); +} + +void +_pbcA_open_heap(pbc_array _array, struct heap *h) { + struct array * a = (struct array *)_array; + a->number = 0; + a->heap = h; + a->a = (union _pbc_var *)(a+1); +} + +void +_pbcA_close(pbc_array _array) { + struct array * a = (struct array *)_array; + if (a->heap == NULL && a->a != NULL && (union _pbc_var *)(a+1) != a->a) { + _pbcM_free(a->a); + a->a = NULL; + } +} + +void +_pbcA_push(pbc_array _array, pbc_var var) { + struct array * a = (struct array *)_array; + if (a->number == 0) { + a->a = (union _pbc_var *)(a+1); + } else if (a->number >= INNER_FIELD) { + if (a->number == INNER_FIELD) { + int cap = 1; + while (cap <= a->number + 1) + cap *= 2; + struct heap * h = a->heap; + union _pbc_var * outer = (union _pbc_var *)HMALLOC(cap * sizeof(union _pbc_var)); + memcpy(outer , a->a , INNER_FIELD * sizeof(pbc_var)); + a->a = outer; + } else { + int size=a->number; + if (((size + 1) ^ size) > size) { + struct heap * h = a->heap; + if (h) { + void * old = a->a; + a->a = (union _pbc_var *)_pbcH_alloc(h, sizeof(union _pbc_var) * (size+1) * 2); + memcpy(a->a, old, sizeof(union _pbc_var) * size); + } else { + a->a = (union _pbc_var *)_pbcM_realloc(a->a,sizeof(union _pbc_var) * (size+1) * 2); + } + } + } + } + a->a[a->number] = *var; + ++ a->number; +} + +void +_pbcA_index(pbc_array _array, int idx, pbc_var var) +{ + struct array * a = (struct array *)_array; + var[0] = a->a[idx]; +} + +void * +_pbcA_index_p(pbc_array _array, int idx) +{ + struct array * a = (struct array *)_array; + return &(a->a[idx]); +} + +int +pbc_array_size(pbc_array _array) { + struct array * a = (struct array *)_array; + return a->number; +} + +uint32_t +pbc_array_integer(pbc_array array, int index, uint32_t *hi) { + pbc_var var; + _pbcA_index(array , index , var); + if (hi) { + *hi = var->integer.hi; + } + return var->integer.low; +} + +double +pbc_array_real(pbc_array array, int index) { + pbc_var var; + _pbcA_index(array , index , var); + return var->real; +} + +struct pbc_slice * +pbc_array_slice(pbc_array _array, int index) { + struct array * a = (struct array *)_array; + if (index <0 || index > a->number) { + return NULL; + } + return (struct pbc_slice *) &(a->a[index]); +} + +void +pbc_array_push_integer(pbc_array array, uint32_t low, uint32_t hi) { + pbc_var var; + var->integer.low = low; + var->integer.hi = hi; + _pbcA_push(array,var); +} + +void +pbc_array_push_slice(pbc_array array, struct pbc_slice *s) { + pbc_var var; + var->m = *s; + _pbcA_push(array,var); +} + +void +pbc_array_push_real(pbc_array array, double v) { + pbc_var var; + var->real = v; + _pbcA_push(array,var); +} diff --git a/luaclib/pb/src/array.h b/luaclib/pb/src/array.h new file mode 100644 index 0000000..cc7f0a9 --- /dev/null +++ b/luaclib/pb/src/array.h @@ -0,0 +1,31 @@ +#ifndef PROTOBUF_C_ARRAY_H +#define PROTOBUF_C_ARRAY_H + +#include "varint.h" +#include "pbc.h" +#include "alloc.h" + +typedef union _pbc_var { + struct longlong integer; + double real; + struct { + const char * str; + int len; + } s; + struct { + int id; + const char * name; + } e; + struct pbc_slice m; + void * p[2]; +} pbc_var[1]; + +void _pbcA_open(pbc_array); +void _pbcA_open_heap(pbc_array, struct heap *h); +void _pbcA_close(pbc_array); + +void _pbcA_push(pbc_array, pbc_var var); +void _pbcA_index(pbc_array , int idx, pbc_var var); +void * _pbcA_index_p(pbc_array _array, int idx); + +#endif diff --git a/luaclib/pb/src/bootstrap.c b/luaclib/pb/src/bootstrap.c new file mode 100644 index 0000000..f57b75b --- /dev/null +++ b/luaclib/pb/src/bootstrap.c @@ -0,0 +1,303 @@ +#include "pbc.h" +#include "map.h" +#include "context.h" +#include "pattern.h" +#include "proto.h" +#include "alloc.h" +#include "bootstrap.h" +#include "stringpool.h" +#include "array.h" +#include "descriptor.pbc.h" + +#include +#include +#include +#include + +/* + +// Descriptor + +// google.protobuf.Descriptor.proto encoded in descriptor.pbc.h with proto pbc.file . + +package pbc; + +message field { + optional string name = 1; + optional int32 id = 2; + optional int32 label = 3; // 0 optional 1 required 2 repeated + optional int32 type = 4; // type_id + optional string type_name = 5; + optional int32 default_int = 6; + optional string default_string = 7; + optional double default_real = 8; +} + +message file { + optional string name = 1; + repeated string dependency = 2; + + repeated string message_name = 3; + repeated int32 message_size = 4; + repeated field message_field = 5; + + repeated string enum_name = 6; + repeated int32 enum_size = 7; + repeated string enum_string = 8; + repeated int32 enum_id = 9; +} + +*/ + +struct field_t { + struct pbc_slice name; + int32_t id; + int32_t label; + int32_t type; + struct pbc_slice type_name; + int32_t default_integer; + struct pbc_slice default_string; + double default_real; +}; + +struct file_t { + struct pbc_slice name; // string + pbc_array dependency; // string + pbc_array message_name; // string + pbc_array message_size; // int32 + pbc_array message_field; // field_t + pbc_array enum_name; // string + pbc_array enum_size; // int32 + pbc_array enum_string; // string + pbc_array enum_id; // int32 +}; + +static void +set_enum_one(struct pbc_env *p, struct file_t *file, const char *name, int start, int sz) { + struct map_kv *table = (struct map_kv *)malloc(sz * sizeof(struct map_kv)); + int i; + for (i=0;ienum_id, start+i, id); + _pbcA_index(file->enum_string, start+i, string); + table[i].id = (int)id->integer.low; + table[i].pointer = (void *)string->s.str; + } + _pbcP_push_enum(p,name,table,sz); + + free(table); +} + +static void +set_enums(struct pbc_env *p, struct file_t *file) { + int n = pbc_array_size(file->enum_size); + int i; + int start = 0; + for (i=0;ienum_name,i,name); + pbc_var var; + _pbcA_index(file->enum_size,i,var); + set_enum_one(p, file, name->s.str, start , (int)var->integer.low); + start += var->integer.low; + } +} + +static void +set_default(struct _field *f, struct field_t *input) { + switch (f->type) { + case PTYPE_DOUBLE: + case PTYPE_FLOAT: + f->default_v->real = input->default_real; + break; + case PTYPE_STRING: + case PTYPE_ENUM: + f->default_v->m = input->default_string; + break; + default: + f->default_v->integer.low = input->default_integer; + break; + } +} + +static void +set_msg_one(struct pbc_pattern * FIELD_T, struct pbc_env *p, struct file_t *file, const char *name, int start, int sz , pbc_array queue) { + int i; + for (i=0;imessage_field, start+i, _field); + struct field_t field; + + int ret = pbc_pattern_unpack(FIELD_T, &_field->m, &field); + if (ret != 0) { + continue; + } + struct _field f; + f.id = field.id; + f.name = (const char *)field.name.buffer; + f.type = field.type; + f.label = field.label; + f.type_name.n = (const char *)field.type_name.buffer; + set_default(&f, &field); + + _pbcP_push_message(p,name, &f , queue); + + // don't need to close pattern since no array + } + _pbcP_init_message(p, name); +} + +static void +set_msgs(struct pbc_pattern * FIELD_T, struct pbc_env *p, struct file_t *file , pbc_array queue) { + int n = pbc_array_size(file->message_size); + int i; + int start = 0; + for (i=0;imessage_name,i,name); + pbc_var sz; + _pbcA_index(file->message_size,i,sz); + set_msg_one(FIELD_T, p, file, name->s.str, start , (int)sz->integer.low , queue); + start += sz->integer.low; + } +} + +static void +set_field_one(struct pbc_env *p, struct _field *f) { + const char * type_name = f->type_name.n; + if (f->type == PTYPE_MESSAGE) { + f->type_name.m = (struct _message *)_pbcM_sp_query(p->msgs, type_name); +// printf("MESSAGE: %s %p\n",type_name, f->type_name.m); + } else if (f->type == PTYPE_ENUM) { + f->type_name.e = (struct _enum *)_pbcM_sp_query(p->enums, type_name); +// printf("ENUM: %s %p ",type_name, f->type_name.e); + const char * str = f->default_v->s.str; + if (str && str[0]) { + int err = _pbcM_si_query(f->type_name.e->name, str , &(f->default_v->e.id)); + if (err < 0) + goto _default; + f->default_v->e.name = (const char *)_pbcM_ip_query(f->type_name.e->id, f->default_v->e.id); +// printf("[%s %d]\n",str,f->default_v->e.id); + } else { +_default: + memcpy(f->default_v, f->type_name.e->default_v, sizeof(pbc_var)); +// printf("(%s %d)\n",f->default_v->e.name,f->default_v->e.id); + } + } +} + +void +_pbcB_register_fields(struct pbc_env *p, pbc_array queue) { + int sz = pbc_array_size(queue); + int i; + for (i=0;im.buffer; + set_field_one(p, f); + } +} + +static void +_set_string(struct _pattern_field * f) { + f->ptype = PTYPE_STRING; + f->ctype = CTYPE_VAR; + f->defv->s.str = ""; + f->defv->s.len = 0; +} + +static void +_set_int32(struct _pattern_field * f) { + f->ptype = PTYPE_INT32; + f->ctype = CTYPE_INT32; +} + +static void +_set_double(struct _pattern_field * f) { + f->ptype = PTYPE_DOUBLE; + f->ctype = CTYPE_DOUBLE; +} + +static void +_set_message_array(struct _pattern_field *f) { + f->ptype = PTYPE_MESSAGE; + f->ctype = CTYPE_ARRAY; +} + +static void +_set_string_array(struct _pattern_field * f) { + f->ptype = PTYPE_STRING; + f->ctype = CTYPE_ARRAY; +} + +static void +_set_int32_array(struct _pattern_field * f) { + f->ptype = PTYPE_INT32; + f->ctype = CTYPE_ARRAY; +} + +#define SET_PATTERN(pat , idx , pat_type, field_name , type) \ + pat->f[idx].id = idx+1 ; \ + pat->f[idx].offset = offsetof(struct pat_type, field_name); \ + _set_##type(&pat->f[idx]); + +#define F(idx,field_name,type) SET_PATTERN(FIELD_T, idx, field_t ,field_name, type) +#define D(idx,field_name,type) SET_PATTERN(FILE_T, idx, file_t ,field_name, type) + +static int +register_internal(struct pbc_env * p, struct pbc_slice *slice) { + struct pbc_pattern * FIELD_T = _pbcP_new(p,8); + F(0,name,string); + F(1,id,int32); + F(2,label,int32); + F(3,type,int32); + F(4,type_name,string); + F(5,default_integer,int32); + F(6,default_string,string); + F(7,default_real,double); + + struct pbc_pattern * FILE_T = _pbcP_new(p,10); + + D(0,name,string); + D(1,dependency,string_array); + D(2,message_name,string_array); + D(3,message_size,int32_array); + D(4,message_field,message_array); + D(5,enum_name,string_array); + D(6,enum_size,int32_array); + D(7,enum_string,string_array); + D(8,enum_id,int32_array); + + int ret = 0; + + struct file_t file; + int r = pbc_pattern_unpack(FILE_T, slice, &file); + if (r != 0) { + ret = 1; + goto _return; + } + + _pbcM_sp_insert(p->files , (const char *)file.name.buffer, NULL); + + pbc_array queue; + _pbcA_open(queue); + + set_enums(p, &file); + set_msgs(FIELD_T, p, &file, queue); + _pbcB_register_fields(p, queue); + + _pbcA_close(queue); + pbc_pattern_close_arrays(FILE_T, &file); + +_return: + free(FIELD_T); + free(FILE_T); + return ret; +} + +void +_pbcB_init(struct pbc_env * p) { + struct pbc_slice slice = { pbc_descriptor,sizeof(pbc_descriptor) }; + register_internal(p,&slice); +} diff --git a/luaclib/pb/src/bootstrap.h b/luaclib/pb/src/bootstrap.h new file mode 100644 index 0000000..48566f5 --- /dev/null +++ b/luaclib/pb/src/bootstrap.h @@ -0,0 +1,10 @@ +#ifndef PROTOBUF_C_BOOTSTRAP_H +#define PROTOBUF_C_BOOTSTRAP_H + +#include "proto.h" +#include "pbc.h" + +void _pbcB_init(struct pbc_env *); +void _pbcB_register_fields(struct pbc_env *, pbc_array queue); + +#endif diff --git a/luaclib/pb/src/context.c b/luaclib/pb/src/context.c new file mode 100644 index 0000000..87bbf93 --- /dev/null +++ b/luaclib/pb/src/context.c @@ -0,0 +1,280 @@ +#include "pbc.h" +#include "alloc.h" +#include "varint.h" +#include "context.h" + +#include +#include +#include +#ifndef _MSC_VER +#include +#endif + +#define INNER_ATOM ((PBC_CONTEXT_CAP - sizeof(struct context)) / sizeof(struct atom)) + +static char * +wiretype_decode(uint8_t *buffer, int cap , struct atom *a , int start) +{ + uint8_t temp[10]; + struct longlong r; + int len; + if (cap >= 10) { + len = _pbcV_decode(buffer, &r); + if (r.hi !=0) + return NULL; + } else { + memcpy(temp, buffer , cap); + len = _pbcV_decode(temp, &r); + if (len > cap || r.hi !=0) + return NULL; + } + + int wiretype = r.low & 7; + a->wire_id = r.low; + buffer += len; + start += len; + cap -=len; + + switch (wiretype) { + case WT_VARINT : + if (cap >=10) { + len = _pbcV_decode(buffer, &a->v.i); + } else { + memcpy(temp, buffer , cap); + len = _pbcV_decode(temp, &a->v.i); + if (cap < len) + return NULL; + } + return (char *)buffer+len; + case WT_BIT64 : + if (cap < 8) + return NULL; + a->v.i.low = buffer[0] | + buffer[1] << 8 | + buffer[2] << 16 | + buffer[3] << 24; + a->v.i.hi = buffer[4] | + buffer[5] << 8 | + buffer[6] << 16 | + buffer[7] << 24; + return (char *)buffer + 8; + case WT_LEND : + if (cap >=10) { + len = _pbcV_decode(buffer, &r); + } else { + memcpy(temp, buffer , cap); + len = _pbcV_decode(temp, &r); + } + if (cap < len + r.low || r.hi !=0) + return NULL; + a->v.s.start = start + len; + a->v.s.end = start + len + r.low; + return (char *)buffer + len + r.low; + case WT_BIT32 : + if (cap < 4) + return NULL; + a->v.i.low = buffer[0] | + buffer[1] << 8 | + buffer[2] << 16 | + buffer[3] << 24; + a->v.i.hi = 0; + return (char *)buffer + 4; + default: + return NULL; + } +} + +static inline int +_decode_varint(uint8_t * buffer, int size , struct atom * a) { + a->wire_id = WT_VARINT; + if (size < 10) { + uint8_t temp[10]; + memcpy(temp,buffer,size); + return _pbcV_decode(temp , &(a->v.i)); + } else { + return _pbcV_decode(buffer , &(a->v.i)); + } +} + +static int +_open_packed_varint(struct context * ctx , uint8_t * buffer, int size) { + struct atom * a = (struct atom *)(ctx + 1); + + int i; + + for (i=0;ia = a; + } else { + int cap = 64; + ctx->a = (struct atom *)malloc(cap * sizeof(struct atom)); + while (size > 0) { + if (i >= cap) { + cap = cap + 64; + ctx->a = (struct atom *)realloc(ctx->a, cap * sizeof(struct atom)); + continue; + } + int len = _decode_varint(buffer, size, &a[i]); + buffer += len; + size -= len; + + ++i; + } + memcpy(ctx->a, a , sizeof(struct atom) * INNER_ATOM); + } + ctx->number = i; + + return i; +} + +int +_pbcC_open_packed(pbc_ctx _ctx, int ptype, void *buffer, int size) { + struct context * ctx = (struct context *)_ctx; + ctx->buffer = (char *)buffer; + ctx->size = size; + ctx->number = 0; + ctx->a = NULL; + + if (buffer == NULL || size == 0) { + return 0; + } + + int bits = 0; + + switch (ptype) { + case PTYPE_INT64: + case PTYPE_UINT64: + case PTYPE_INT32: + case PTYPE_BOOL: + case PTYPE_UINT32: + case PTYPE_ENUM: + case PTYPE_SINT32: + case PTYPE_SINT64: + return _open_packed_varint(ctx , (uint8_t *)buffer, size); + case PTYPE_DOUBLE: + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + ctx->number = size / 8; + bits = 64; + break; + case PTYPE_FLOAT: + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + ctx->number = size / 4; + bits = 32; + break; + default: + return 0; + } + + struct atom * a = (struct atom *)(ctx + 1); + + if (ctx->number > INNER_ATOM) { + ctx->a = (struct atom *)malloc(ctx->number * sizeof(struct atom)); + a = ctx->a; + } else { + ctx->a = a; + } + + int i; + if (bits == 64) { + uint8_t * data = (uint8_t *)buffer; + for (i=0;inumber;i++) { + a[i].wire_id = WT_BIT64; + a[i].v.i.low = data[0] | + data[1] << 8 | + data[2] << 16 | + data[3] << 24; + a[i].v.i.hi = data[4] | + data[5] << 8 | + data[6] << 16 | + data[7] << 24; + data += 8; + } + } else { + uint8_t * data = (uint8_t *)buffer; + for (i=0;inumber;i++) { + a[i].wire_id = WT_BIT32; + a[i].v.i.low = data[0] | + data[1] << 8 | + data[2] << 16 | + data[3] << 24; + a[i].v.i.hi = 0; + data += 4; + } + } + + return ctx->number; +} + +int +_pbcC_open(pbc_ctx _ctx , void *buffer, int size) { + struct context * ctx = (struct context *)_ctx; + ctx->buffer = (char *)buffer; + ctx->size = size; + + if (buffer == NULL || size == 0) { + ctx->number = 0; + ctx->a = NULL; + return 0; + } + + struct atom * a = (struct atom *)(ctx + 1); + + int i; + int start = 0; + + ctx->a = a; + + for (i=0;i 0) { + int cap = 64; + ctx->a = (struct atom *)malloc(cap * sizeof(struct atom)); + while (size > 0) { + if (i >= cap) { + cap = cap + 64; + ctx->a = (struct atom *)realloc(ctx->a, cap * sizeof(struct atom)); + continue; + } + char * next = wiretype_decode((uint8_t *)buffer, size , &ctx->a[i] , start); + if (next == NULL) { + return -i; + } + start += next - (char *)buffer; + size -= next - (char *)buffer; + buffer = next; + ++i; + } + memcpy(ctx->a, a , sizeof(struct atom) * INNER_ATOM); + } + ctx->number = i; + + return i; +} + + +void +_pbcC_close(pbc_ctx _ctx) { + struct context * ctx = (struct context *)_ctx; + if (ctx->a != NULL && (struct atom *)(ctx+1) != ctx->a) { + free(ctx->a); + ctx->a = NULL; + } +} diff --git a/luaclib/pb/src/context.h b/luaclib/pb/src/context.h new file mode 100644 index 0000000..e19ed91 --- /dev/null +++ b/luaclib/pb/src/context.h @@ -0,0 +1,140 @@ +#ifndef PROTOBUF_C_CONTEXT_H +#define PROTOBUF_C_CONTEXT_H + +#include + +#include "array.h" + +#define PBC_CONTEXT_CAP 256 + +// wiretype + +#define WT_VARINT 0 +#define WT_BIT64 1 +#define WT_LEND 2 +#define WT_BIT32 5 + +#define CTYPE_INT32 1 +#define CTYPE_INT64 2 +#define CTYPE_DOUBLE 3 +#define CTYPE_FLOAT 4 +#define CTYPE_POINTER 5 +#define CTYPE_BOOL 6 +#define CTYPE_INT8 7 +#define CTYPE_INT16 8 +#define CTYPE_ARRAY 9 +#define CTYPE_VAR 10 +#define CTYPE_PACKED 11 + +#define PTYPE_DOUBLE 1 +#define PTYPE_FLOAT 2 +#define PTYPE_INT64 3 // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if negative values are likely. +#define PTYPE_UINT64 4 +#define PTYPE_INT32 5 // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if negative values are likely. +#define PTYPE_FIXED64 6 +#define PTYPE_FIXED32 7 +#define PTYPE_BOOL 8 +#define PTYPE_STRING 9 +#define PTYPE_GROUP 10 // Tag-delimited aggregate. +#define PTYPE_MESSAGE 11 // Length-delimited aggregate. +#define PTYPE_BYTES 12 +#define PTYPE_UINT32 13 +#define PTYPE_ENUM 14 +#define PTYPE_SFIXED32 15 +#define PTYPE_SFIXED64 16 +#define PTYPE_SINT32 17 // Uses ZigZag encoding. +#define PTYPE_SINT64 18 // Uses ZigZag encoding. + +struct slice { + int start; + int end; +}; + +struct atom { + int wire_id; + union { + struct slice s; + struct longlong i; + } v; +}; + +struct context { + char * buffer; + int size; + int number; + struct atom * a; +}; + +typedef struct _pbc_ctx { char _data[PBC_CONTEXT_CAP]; } pbc_ctx[1]; + +int _pbcC_open(pbc_ctx , void *buffer, int size); // <=0 failed +int _pbcC_open_packed(pbc_ctx _ctx, int ptype, void *buffer, int size); +void _pbcC_close(pbc_ctx); + +static inline double +read_double(struct atom * a) { + union { + uint64_t i; + double d; + } u; + u.i = (uint64_t) a->v.i.low | (uint64_t) a->v.i.hi << 32; + return u.d; +} + +static inline float +read_float(struct atom * a) { + union { + uint32_t i; + float f; + } u; + u.i = a->v.i.low; + return u.f; +} + +static inline void +double_encode(double v , uint8_t * buffer) { + union { + double v; + uint64_t e; + } u; + u.v = v; + buffer[0] = (uint8_t) (u.e & 0xff); + buffer[1] = (uint8_t) (u.e >> 8 & 0xff); + buffer[2] = (uint8_t) (u.e >> 16 & 0xff); + buffer[3] = (uint8_t) (u.e >> 24 & 0xff); + buffer[4] = (uint8_t) (u.e >> 32 & 0xff); + buffer[5] = (uint8_t) (u.e >> 40 & 0xff); + buffer[6] = (uint8_t) (u.e >> 48 & 0xff); + buffer[7] = (uint8_t) (u.e >> 56 & 0xff); +} + +static inline void +float_encode(float v , uint8_t * buffer) { + union { + float v; + uint32_t e; + } u; + u.v = v; + buffer[0] = (uint8_t) (u.e & 0xff); + buffer[1] = (uint8_t) (u.e >> 8 & 0xff); + buffer[2] = (uint8_t) (u.e >> 16 & 0xff); + buffer[3] = (uint8_t) (u.e >> 24 & 0xff); +} + +#define CHECK_LEND(a,err) if ((a->wire_id & 7) != WT_LEND) return err; + +#if 0 +/* maybe we don't need check these wire type */ +#define CHECK_VARINT(a,err) if ((a->wire_id & 7) != WT_VARINT) return err; +#define CHECK_BIT32(a,err) if ((a->wire_id & 7) != WT_BIT32) return err; +#define CHECK_BIT64(a,err) if ((a->wire_id & 7) != WT_BIT64) return err; + +#else + +#define CHECK_VARINT(a,err) +#define CHECK_BIT32(a,err) +#define CHECK_BIT64(a,err) + +#endif + +#endif diff --git a/luaclib/pb/src/decode.c b/luaclib/pb/src/decode.c new file mode 100644 index 0000000..f8b2f50 --- /dev/null +++ b/luaclib/pb/src/decode.c @@ -0,0 +1,349 @@ +#include "pbc.h" +#include "alloc.h" +#include "context.h" +#include "proto.h" +#include "varint.h" + +#include + +static const char * TYPENAME[] = { + "invalid", // 0 + "integer", // 1 + "real", // 2 + "boolean", // 3 + "enum", // 4 + "string", // 5 + "message", // 6 + "fixed64", // 7 + "fixed32", // 8 + "bytes", // 9 + "int64", // 10 + "uint", // 11 +}; + +static int +call_unknown(pbc_decoder f, void * ud, int id, struct atom *a, uint8_t * start) { + union pbc_value v; + switch (a->wire_id & 7) { + case WT_VARINT: + v.i.low = a->v.i.low; + v.i.hi = a->v.i.hi; + f(ud, PBC_INT, TYPENAME[PBC_INT], &v, id , NULL); + break; + case WT_BIT64: + v.i.low = a->v.i.low; + v.i.hi = a->v.i.hi; + f(ud, PBC_FIXED64, TYPENAME[PBC_FIXED64], &v, id , NULL); + break; + case WT_LEND: + v.s.buffer = (char*)start + a->v.s.start; + v.s.len = a->v.s.end - a->v.s.start; + f(ud, PBC_BYTES, TYPENAME[PBC_BYTES], &v, id , NULL); + break; + case WT_BIT32: + v.i.low = a->v.i.low; + v.i.hi = 0; + f(ud, PBC_FIXED32, TYPENAME[PBC_FIXED32], &v, id , NULL); + break; + default: + return 1; + } + return 0; +} + +static int +call_type(pbc_decoder pd, void * ud, struct _field *f, struct atom *a, uint8_t * start) { + union pbc_value v; + const char * type_name = NULL; + int type = _pbcP_type(f, &type_name); + assert(type != 0); + if (type_name == NULL) { + type_name = TYPENAME[type & ~PBC_REPEATED]; + } + switch (f->type) { + case PTYPE_DOUBLE: + CHECK_BIT64(a, -1); + v.f = read_double(a); + break; + case PTYPE_FLOAT: + CHECK_BIT32(a, -1); + v.f = (double) read_float(a); + break; + case PTYPE_ENUM: + CHECK_VARINT(a, -1); + v.e.id = a->v.i.low; + v.e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , v.e.id); + break; + case PTYPE_INT64: + case PTYPE_UINT64: + CHECK_VARINT(a, -1); + v.i.low = a->v.i.low; + v.i.hi = a->v.i.hi; + break; + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + CHECK_BIT64(a, -1); + v.i.low = a->v.i.low; + v.i.hi = a->v.i.hi; + break; + case PTYPE_INT32: + case PTYPE_UINT32: + case PTYPE_BOOL: + CHECK_VARINT(a, -1); + v.i.low = a->v.i.low; + v.i.hi = 0; + break; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + CHECK_BIT32(a, -1); + v.i.low = a->v.i.low; + v.i.hi = 0; + break; + case PTYPE_SINT32: + CHECK_VARINT(a, -1); + v.i.low = a->v.i.low; + v.i.hi = a->v.i.hi; + _pbcV_dezigzag32((struct longlong *)&(v.i)); + break; + case PTYPE_SINT64: + CHECK_VARINT(a, -1); + v.i.low = a->v.i.low; + v.i.hi = a->v.i.hi; + _pbcV_dezigzag64((struct longlong *)&(v.i)); + break; + case PTYPE_STRING: + case PTYPE_BYTES: + case PTYPE_MESSAGE: + CHECK_LEND(a, -1); + v.s.buffer = start + a->v.s.start; + v.s.len = a->v.s.end - a->v.s.start; + break; + default: + assert(0); + break; + } + pd(ud, type, type_name, &v, f->id, f->name); + return 0; +} + +static int +call_array(pbc_decoder pd, void * ud, struct _field *f, uint8_t * buffer , int size) { + union pbc_value v; + const char * type_name = NULL; + int type = _pbcP_type(f, &type_name); + assert(type != 0); + if (type_name == NULL) { + type_name = TYPENAME[type & ~PBC_REPEATED]; + } + v.i.hi = 0; + int i; + switch(f->type) { + case PTYPE_DOUBLE: + if (size % 8 != 0) { + return -1; + } + for (i=0;iid, f->name); + } + return size/8; + case PTYPE_FLOAT: + if (size % 4 != 0) + return -1; + for (i=0;iid, f->name); + } + return size/4; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + if (size % 4 != 0) + return -1; + for (i=0;iid, f->name); + } + return size/4; + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + if (size % 8 != 0) + return -1; + for (i=0;iid, f->name); + } + return size/8; + case PTYPE_INT64: + case PTYPE_UINT64: + case PTYPE_INT32: + case PTYPE_UINT32: + case PTYPE_BOOL: { + int n = 0; + while (size > 0) { + int len; + if (size >= 10) { + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + } else { + uint8_t temp[10]; + memcpy(temp, buffer, size); + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + if (len > size) + return -1; + } + pd(ud, type , type_name, &v, f->id, f->name); + buffer += len; + size -= len; + ++n; + } + return n; + } + case PTYPE_ENUM: { + int n = 0; + while (size > 0) { + int len; + if (size >= 10) { + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + } else { + uint8_t temp[10]; + memcpy(temp, buffer, size); + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + if (len > size) + return -1; + } + v.e.id = v.i.low; + v.e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , v.i.low); + pd(ud, type , type_name, &v, f->id, f->name); + buffer += len; + size -= len; + ++n; + } + return n; + } + case PTYPE_SINT32: { + int n = 0; + while (size > 0) { + int len; + if (size >= 10) { + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + _pbcV_dezigzag32((struct longlong *)&(v.i)); + } else { + uint8_t temp[10]; + memcpy(temp, buffer, size); + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + if (len > size) + return -1; + _pbcV_dezigzag32((struct longlong *)&(v.i)); + } + pd(ud, type , type_name, &v, f->id, f->name); + buffer += len; + size -= len; + ++n; + } + return n; + } + case PTYPE_SINT64: { + int n = 0; + while (size > 0) { + int len; + if (size >= 10) { + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + _pbcV_dezigzag64((struct longlong *)&(v.i)); + } else { + uint8_t temp[10]; + memcpy(temp, buffer, size); + len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); + if (len > size) + return -1; + _pbcV_dezigzag64((struct longlong *)&(v.i)); + } + pd(ud, type , type_name, &v, f->id, f->name); + buffer += len; + size -= len; + ++n; + } + return n; + } + default: + return -1; + } +} + +int +pbc_decode(struct pbc_env * env, const char * type_name , struct pbc_slice * slice, pbc_decoder pd, void *ud) { + struct _message * msg = _pbcP_get_message(env, type_name); + if (msg == NULL) { + env->lasterror = "Proto not found"; + return -1; + } + if (slice->len == 0) { + return 0; + } + pbc_ctx _ctx; + int count = _pbcC_open(_ctx,slice->buffer,slice->len); + if (count <= 0) { + env->lasterror = "decode context error"; + _pbcC_close(_ctx); + return count - 1; + } + struct context * ctx = (struct context *)_ctx; + uint8_t * start = (uint8_t *)slice->buffer; + + int i; + for (i=0;inumber;i++) { + int id = ctx->a[i].wire_id >> 3; + struct _field * f = (struct _field *)_pbcM_ip_query(msg->id , id); + if (f==NULL) { + int err = call_unknown(pd,ud,id,&ctx->a[i],start); + if (err) { + _pbcC_close(_ctx); + return -i-1; + } + } else if (f->label == LABEL_PACKED) { + struct atom * a = &ctx->a[i]; + int n = call_array(pd, ud, f , start + a->v.s.start , a->v.s.end - a->v.s.start); + if (n < 0) { + _pbcC_close(_ctx); + return -i-1; + } + } else { + if (call_type(pd,ud,f,&ctx->a[i],start) != 0) { + _pbcC_close(_ctx); + return -i-1; + } + } + } + + _pbcC_close(_ctx); + return ctx->number; +} + diff --git a/luaclib/pb/src/descriptor.pbc.h b/luaclib/pb/src/descriptor.pbc.h new file mode 100644 index 0000000..88dca86 --- /dev/null +++ b/luaclib/pb/src/descriptor.pbc.h @@ -0,0 +1,271 @@ +static unsigned char pbc_descriptor[] = { +72,1,72,2,72,3,72,4,72,5,72,6,72,7,72,8, +72,9,72,10,72,11,72,12,72,13,72,14,72,15,72,16, +72,17,72,18,72,1,72,2,72,3,72,1,72,2,72,3, +72,0,72,1,72,2,50,42,103,111,111,103,108,101,46,112, +114,111,116,111,98,117,102,46,70,105,101,108,100,68,101,115, +99,114,105,112,116,111,114,80,114,111,116,111,46,84,121,112, +101,0,50,43,103,111,111,103,108,101,46,112,114,111,116,111, +98,117,102,46,70,105,101,108,100,68,101,115,99,114,105,112, +116,111,114,80,114,111,116,111,46,76,97,98,101,108,0,50, +41,103,111,111,103,108,101,46,112,114,111,116,111,98,117,102, +46,70,105,108,101,79,112,116,105,111,110,115,46,79,112,116, +105,109,105,122,101,77,111,100,101,0,50,35,103,111,111,103, +108,101,46,112,114,111,116,111,98,117,102,46,70,105,101,108, +100,79,112,116,105,111,110,115,46,67,84,121,112,101,0,66, +12,84,89,80,69,95,68,79,85,66,76,69,0,66,11,84, +89,80,69,95,70,76,79,65,84,0,66,11,84,89,80,69, +95,73,78,84,54,52,0,66,12,84,89,80,69,95,85,73, +78,84,54,52,0,66,11,84,89,80,69,95,73,78,84,51, +50,0,66,13,84,89,80,69,95,70,73,88,69,68,54,52, +0,66,13,84,89,80,69,95,70,73,88,69,68,51,50,0, +66,10,84,89,80,69,95,66,79,79,76,0,66,12,84,89, +80,69,95,83,84,82,73,78,71,0,66,11,84,89,80,69, +95,71,82,79,85,80,0,66,13,84,89,80,69,95,77,69, +83,83,65,71,69,0,66,11,84,89,80,69,95,66,89,84, +69,83,0,66,12,84,89,80,69,95,85,73,78,84,51,50, +0,66,10,84,89,80,69,95,69,78,85,77,0,66,14,84, +89,80,69,95,83,70,73,88,69,68,51,50,0,66,14,84, +89,80,69,95,83,70,73,88,69,68,54,52,0,66,12,84, +89,80,69,95,83,73,78,84,51,50,0,66,12,84,89,80, +69,95,83,73,78,84,54,52,0,66,15,76,65,66,69,76, +95,79,80,84,73,79,78,65,76,0,66,15,76,65,66,69, +76,95,82,69,81,85,73,82,69,68,0,66,15,76,65,66, +69,76,95,82,69,80,69,65,84,69,68,0,66,6,83,80, +69,69,68,0,66,10,67,79,68,69,95,83,73,90,69,0, +66,13,76,73,84,69,95,82,85,78,84,73,77,69,0,66, +7,83,84,82,73,78,71,0,66,5,67,79,82,68,0,66, +13,83,84,82,73,78,71,95,80,73,69,67,69,0,56,18, +56,3,56,3,56,3,10,11,100,101,115,99,114,105,112,116, +111,114,0,32,1,32,9,32,7,32,2,32,8,32,3,32, +3,32,3,32,4,32,9,32,3,32,5,32,1,32,1,32, +1,32,1,32,7,32,2,32,1,32,2,42,51,24,2,16, +1,32,11,42,36,103,111,111,103,108,101,46,112,114,111,116, +111,98,117,102,46,70,105,108,101,68,101,115,99,114,105,112, +116,111,114,80,114,111,116,111,0,10,5,102,105,108,101,0, +42,13,32,9,24,0,10,5,110,97,109,101,0,16,1,42, +16,32,9,24,0,10,8,112,97,99,107,97,103,101,0,16, +2,42,19,32,9,24,2,10,11,100,101,112,101,110,100,101, +110,99,121,0,16,3,42,55,24,2,16,4,32,11,42,32, +103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46, +68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0, +10,13,109,101,115,115,97,103,101,95,116,121,112,101,0,42, +56,24,2,16,5,32,11,42,36,103,111,111,103,108,101,46, +112,114,111,116,111,98,117,102,46,69,110,117,109,68,101,115, +99,114,105,112,116,111,114,80,114,111,116,111,0,10,10,101, +110,117,109,95,116,121,112,101,0,42,57,24,2,16,6,32, +11,42,39,103,111,111,103,108,101,46,112,114,111,116,111,98, +117,102,46,83,101,114,118,105,99,101,68,101,115,99,114,105, +112,116,111,114,80,114,111,116,111,0,10,8,115,101,114,118, +105,99,101,0,42,57,24,2,16,7,32,11,42,37,103,111, +111,103,108,101,46,112,114,111,116,111,98,117,102,46,70,105, +101,108,100,68,101,115,99,114,105,112,116,111,114,80,114,111, +116,111,0,10,10,101,120,116,101,110,115,105,111,110,0,42, +46,24,0,16,8,32,11,42,28,103,111,111,103,108,101,46, +112,114,111,116,111,98,117,102,46,70,105,108,101,79,112,116, +105,111,110,115,0,10,8,111,112,116,105,111,110,115,0,42, +58,24,0,16,9,32,11,42,31,103,111,111,103,108,101,46, +112,114,111,116,111,98,117,102,46,83,111,117,114,99,101,67, +111,100,101,73,110,102,111,0,10,17,115,111,117,114,99,101, +95,99,111,100,101,95,105,110,102,111,0,42,13,32,9,24, +0,10,5,110,97,109,101,0,16,1,42,53,24,2,16,2, +32,11,42,37,103,111,111,103,108,101,46,112,114,111,116,111, +98,117,102,46,70,105,101,108,100,68,101,115,99,114,105,112, +116,111,114,80,114,111,116,111,0,10,6,102,105,101,108,100, +0,42,57,24,2,16,6,32,11,42,37,103,111,111,103,108, +101,46,112,114,111,116,111,98,117,102,46,70,105,101,108,100, +68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0, +10,10,101,120,116,101,110,115,105,111,110,0,42,54,24,2, +16,3,32,11,42,32,103,111,111,103,108,101,46,112,114,111, +116,111,98,117,102,46,68,101,115,99,114,105,112,116,111,114, +80,114,111,116,111,0,10,12,110,101,115,116,101,100,95,116, +121,112,101,0,42,56,24,2,16,4,32,11,42,36,103,111, +111,103,108,101,46,112,114,111,116,111,98,117,102,46,69,110, +117,109,68,101,115,99,114,105,112,116,111,114,80,114,111,116, +111,0,10,10,101,110,117,109,95,116,121,112,101,0,42,73, +24,2,16,5,32,11,42,47,103,111,111,103,108,101,46,112, +114,111,116,111,98,117,102,46,68,101,115,99,114,105,112,116, +111,114,80,114,111,116,111,46,69,120,116,101,110,115,105,111, +110,82,97,110,103,101,0,10,16,101,120,116,101,110,115,105, +111,110,95,114,97,110,103,101,0,42,49,24,0,16,7,32, +11,42,31,103,111,111,103,108,101,46,112,114,111,116,111,98, +117,102,46,77,101,115,115,97,103,101,79,112,116,105,111,110, +115,0,10,8,111,112,116,105,111,110,115,0,42,14,32,5, +24,0,10,6,115,116,97,114,116,0,16,1,42,12,32,5, +24,0,10,4,101,110,100,0,16,2,42,13,32,9,24,0, +10,5,110,97,109,101,0,16,1,42,15,32,5,24,0,10, +7,110,117,109,98,101,114,0,16,3,42,59,24,0,16,4, +32,14,42,43,103,111,111,103,108,101,46,112,114,111,116,111, +98,117,102,46,70,105,101,108,100,68,101,115,99,114,105,112, +116,111,114,80,114,111,116,111,46,76,97,98,101,108,0,10, +6,108,97,98,101,108,0,42,57,24,0,16,5,32,14,42, +42,103,111,111,103,108,101,46,112,114,111,116,111,98,117,102, +46,70,105,101,108,100,68,101,115,99,114,105,112,116,111,114, +80,114,111,116,111,46,84,121,112,101,0,10,5,116,121,112, +101,0,42,18,32,9,24,0,10,10,116,121,112,101,95,110, +97,109,101,0,16,6,42,17,32,9,24,0,10,9,101,120, +116,101,110,100,101,101,0,16,2,42,22,32,9,24,0,10, +14,100,101,102,97,117,108,116,95,118,97,108,117,101,0,16, +7,42,47,24,0,16,8,32,11,42,29,103,111,111,103,108, +101,46,112,114,111,116,111,98,117,102,46,70,105,101,108,100, +79,112,116,105,111,110,115,0,10,8,111,112,116,105,111,110, +115,0,42,13,32,9,24,0,10,5,110,97,109,101,0,16, +1,42,57,24,2,16,2,32,11,42,41,103,111,111,103,108, +101,46,112,114,111,116,111,98,117,102,46,69,110,117,109,86, +97,108,117,101,68,101,115,99,114,105,112,116,111,114,80,114, +111,116,111,0,10,6,118,97,108,117,101,0,42,46,24,0, +16,3,32,11,42,28,103,111,111,103,108,101,46,112,114,111, +116,111,98,117,102,46,69,110,117,109,79,112,116,105,111,110, +115,0,10,8,111,112,116,105,111,110,115,0,42,13,32,9, +24,0,10,5,110,97,109,101,0,16,1,42,15,32,5,24, +0,10,7,110,117,109,98,101,114,0,16,2,42,51,24,0, +16,3,32,11,42,33,103,111,111,103,108,101,46,112,114,111, +116,111,98,117,102,46,69,110,117,109,86,97,108,117,101,79, +112,116,105,111,110,115,0,10,8,111,112,116,105,111,110,115, +0,42,13,32,9,24,0,10,5,110,97,109,101,0,16,1, +42,55,24,2,16,2,32,11,42,38,103,111,111,103,108,101, +46,112,114,111,116,111,98,117,102,46,77,101,116,104,111,100, +68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0, +10,7,109,101,116,104,111,100,0,42,49,24,0,16,3,32, +11,42,31,103,111,111,103,108,101,46,112,114,111,116,111,98, +117,102,46,83,101,114,118,105,99,101,79,112,116,105,111,110, +115,0,10,8,111,112,116,105,111,110,115,0,42,13,32,9, +24,0,10,5,110,97,109,101,0,16,1,42,19,32,9,24, +0,10,11,105,110,112,117,116,95,116,121,112,101,0,16,2, +42,20,32,9,24,0,10,12,111,117,116,112,117,116,95,116, +121,112,101,0,16,3,42,48,24,0,16,4,32,11,42,30, +103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46, +77,101,116,104,111,100,79,112,116,105,111,110,115,0,10,8, +111,112,116,105,111,110,115,0,42,21,32,9,24,0,10,13, +106,97,118,97,95,112,97,99,107,97,103,101,0,16,1,42, +29,32,9,24,0,10,21,106,97,118,97,95,111,117,116,101, +114,95,99,108,97,115,115,110,97,109,101,0,16,8,42,30, +24,0,16,10,32,8,10,20,106,97,118,97,95,109,117,108, +116,105,112,108,101,95,102,105,108,101,115,0,48,0,42,40, +24,0,16,20,32,8,10,30,106,97,118,97,95,103,101,110, +101,114,97,116,101,95,101,113,117,97,108,115,95,97,110,100, +95,104,97,115,104,0,48,0,42,72,24,0,16,9,32,14, +42,41,103,111,111,103,108,101,46,112,114,111,116,111,98,117, +102,46,70,105,108,101,79,112,116,105,111,110,115,46,79,112, +116,105,109,105,122,101,77,111,100,101,0,10,13,111,112,116, +105,109,105,122,101,95,102,111,114,0,58,6,83,80,69,69, +68,0,42,30,24,0,16,16,32,8,10,20,99,99,95,103, +101,110,101,114,105,99,95,115,101,114,118,105,99,101,115,0, +48,0,42,32,24,0,16,17,32,8,10,22,106,97,118,97, +95,103,101,110,101,114,105,99,95,115,101,114,118,105,99,101, +115,0,48,0,42,30,24,0,16,18,32,8,10,20,112,121, +95,103,101,110,101,114,105,99,95,115,101,114,118,105,99,101, +115,0,48,0,42,68,24,2,16,231,7,32,11,42,36,103, +111,111,103,108,101,46,112,114,111,116,111,98,117,102,46,85, +110,105,110,116,101,114,112,114,101,116,101,100,79,112,116,105, +111,110,0,10,21,117,110,105,110,116,101,114,112,114,101,116, +101,100,95,111,112,116,105,111,110,0,42,34,24,0,16,1, +32,8,10,24,109,101,115,115,97,103,101,95,115,101,116,95, +119,105,114,101,95,102,111,114,109,97,116,0,48,0,42,42, +24,0,16,2,32,8,10,32,110,111,95,115,116,97,110,100, +97,114,100,95,100,101,115,99,114,105,112,116,111,114,95,97, +99,99,101,115,115,111,114,0,48,0,42,68,24,2,16,231, +7,32,11,42,36,103,111,111,103,108,101,46,112,114,111,116, +111,98,117,102,46,85,110,105,110,116,101,114,112,114,101,116, +101,100,79,112,116,105,111,110,0,10,21,117,110,105,110,116, +101,114,112,114,101,116,101,100,95,111,112,116,105,111,110,0, +42,60,24,0,16,1,32,14,42,35,103,111,111,103,108,101, +46,112,114,111,116,111,98,117,102,46,70,105,101,108,100,79, +112,116,105,111,110,115,46,67,84,121,112,101,0,10,6,99, +116,121,112,101,0,58,7,83,84,82,73,78,71,0,42,15, +32,8,24,0,10,7,112,97,99,107,101,100,0,16,2,42, +21,24,0,16,3,32,8,10,11,100,101,112,114,101,99,97, +116,101,100,0,48,0,42,29,32,9,24,0,10,21,101,120, +112,101,114,105,109,101,110,116,97,108,95,109,97,112,95,107, +101,121,0,16,9,42,68,24,2,16,231,7,32,11,42,36, +103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46, +85,110,105,110,116,101,114,112,114,101,116,101,100,79,112,116, +105,111,110,0,10,21,117,110,105,110,116,101,114,112,114,101, +116,101,100,95,111,112,116,105,111,110,0,42,68,24,2,16, +231,7,32,11,42,36,103,111,111,103,108,101,46,112,114,111, +116,111,98,117,102,46,85,110,105,110,116,101,114,112,114,101, +116,101,100,79,112,116,105,111,110,0,10,21,117,110,105,110, +116,101,114,112,114,101,116,101,100,95,111,112,116,105,111,110, +0,42,68,24,2,16,231,7,32,11,42,36,103,111,111,103, +108,101,46,112,114,111,116,111,98,117,102,46,85,110,105,110, +116,101,114,112,114,101,116,101,100,79,112,116,105,111,110,0, +10,21,117,110,105,110,116,101,114,112,114,101,116,101,100,95, +111,112,116,105,111,110,0,42,68,24,2,16,231,7,32,11, +42,36,103,111,111,103,108,101,46,112,114,111,116,111,98,117, +102,46,85,110,105,110,116,101,114,112,114,101,116,101,100,79, +112,116,105,111,110,0,10,21,117,110,105,110,116,101,114,112, +114,101,116,101,100,95,111,112,116,105,111,110,0,42,68,24, +2,16,231,7,32,11,42,36,103,111,111,103,108,101,46,112, +114,111,116,111,98,117,102,46,85,110,105,110,116,101,114,112, +114,101,116,101,100,79,112,116,105,111,110,0,10,21,117,110, +105,110,116,101,114,112,114,101,116,101,100,95,111,112,116,105, +111,110,0,42,60,24,2,16,2,32,11,42,45,103,111,111, +103,108,101,46,112,114,111,116,111,98,117,102,46,85,110,105, +110,116,101,114,112,114,101,116,101,100,79,112,116,105,111,110, +46,78,97,109,101,80,97,114,116,0,10,5,110,97,109,101, +0,42,25,32,9,24,0,10,17,105,100,101,110,116,105,102, +105,101,114,95,118,97,108,117,101,0,16,3,42,27,32,4, +24,0,10,19,112,111,115,105,116,105,118,101,95,105,110,116, +95,118,97,108,117,101,0,16,4,42,27,32,3,24,0,10, +19,110,101,103,97,116,105,118,101,95,105,110,116,95,118,97, +108,117,101,0,16,5,42,21,32,1,24,0,10,13,100,111, +117,98,108,101,95,118,97,108,117,101,0,16,6,42,21,32, +12,24,0,10,13,115,116,114,105,110,103,95,118,97,108,117, +101,0,16,7,42,24,32,9,24,0,10,16,97,103,103,114, +101,103,97,116,101,95,118,97,108,117,101,0,16,8,42,18, +32,9,24,1,10,10,110,97,109,101,95,112,97,114,116,0, +16,1,42,21,32,8,24,1,10,13,105,115,95,101,120,116, +101,110,115,105,111,110,0,16,2,42,59,24,2,16,1,32, +11,42,40,103,111,111,103,108,101,46,112,114,111,116,111,98, +117,102,46,83,111,117,114,99,101,67,111,100,101,73,110,102, +111,46,76,111,99,97,116,105,111,110,0,10,9,108,111,99, +97,116,105,111,110,0,42,13,32,5,24,2,10,5,112,97, +116,104,0,16,1,42,13,32,5,24,2,10,5,115,112,97, +110,0,16,2,26,34,103,111,111,103,108,101,46,112,114,111, +116,111,98,117,102,46,70,105,108,101,68,101,115,99,114,105, +112,116,111,114,83,101,116,0,26,36,103,111,111,103,108,101, +46,112,114,111,116,111,98,117,102,46,70,105,108,101,68,101, +115,99,114,105,112,116,111,114,80,114,111,116,111,0,26,32, +103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46, +68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0, +26,47,103,111,111,103,108,101,46,112,114,111,116,111,98,117, +102,46,68,101,115,99,114,105,112,116,111,114,80,114,111,116, +111,46,69,120,116,101,110,115,105,111,110,82,97,110,103,101, +0,26,37,103,111,111,103,108,101,46,112,114,111,116,111,98, +117,102,46,70,105,101,108,100,68,101,115,99,114,105,112,116, +111,114,80,114,111,116,111,0,26,36,103,111,111,103,108,101, +46,112,114,111,116,111,98,117,102,46,69,110,117,109,68,101, +115,99,114,105,112,116,111,114,80,114,111,116,111,0,26,41, +103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46, +69,110,117,109,86,97,108,117,101,68,101,115,99,114,105,112, +116,111,114,80,114,111,116,111,0,26,39,103,111,111,103,108, +101,46,112,114,111,116,111,98,117,102,46,83,101,114,118,105, +99,101,68,101,115,99,114,105,112,116,111,114,80,114,111,116, +111,0,26,38,103,111,111,103,108,101,46,112,114,111,116,111, +98,117,102,46,77,101,116,104,111,100,68,101,115,99,114,105, +112,116,111,114,80,114,111,116,111,0,26,28,103,111,111,103, +108,101,46,112,114,111,116,111,98,117,102,46,70,105,108,101, +79,112,116,105,111,110,115,0,26,31,103,111,111,103,108,101, +46,112,114,111,116,111,98,117,102,46,77,101,115,115,97,103, +101,79,112,116,105,111,110,115,0,26,29,103,111,111,103,108, +101,46,112,114,111,116,111,98,117,102,46,70,105,101,108,100, +79,112,116,105,111,110,115,0,26,28,103,111,111,103,108,101, +46,112,114,111,116,111,98,117,102,46,69,110,117,109,79,112, +116,105,111,110,115,0,26,33,103,111,111,103,108,101,46,112, +114,111,116,111,98,117,102,46,69,110,117,109,86,97,108,117, +101,79,112,116,105,111,110,115,0,26,31,103,111,111,103,108, +101,46,112,114,111,116,111,98,117,102,46,83,101,114,118,105, +99,101,79,112,116,105,111,110,115,0,26,30,103,111,111,103, +108,101,46,112,114,111,116,111,98,117,102,46,77,101,116,104, +111,100,79,112,116,105,111,110,115,0,26,36,103,111,111,103, +108,101,46,112,114,111,116,111,98,117,102,46,85,110,105,110, +116,101,114,112,114,101,116,101,100,79,112,116,105,111,110,0, +26,45,103,111,111,103,108,101,46,112,114,111,116,111,98,117, +102,46,85,110,105,110,116,101,114,112,114,101,116,101,100,79, +112,116,105,111,110,46,78,97,109,101,80,97,114,116,0,26, +31,103,111,111,103,108,101,46,112,114,111,116,111,98,117,102, +46,83,111,117,114,99,101,67,111,100,101,73,110,102,111,0, +26,40,103,111,111,103,108,101,46,112,114,111,116,111,98,117, +102,46,83,111,117,114,99,101,67,111,100,101,73,110,102,111, +46,76,111,99,97,116,105,111,110,0, +}; \ No newline at end of file diff --git a/luaclib/pb/src/map.c b/luaclib/pb/src/map.c new file mode 100644 index 0000000..2e859a0 --- /dev/null +++ b/luaclib/pb/src/map.c @@ -0,0 +1,477 @@ +#include "map.h" +#include "alloc.h" + +#include +#include + +struct _pbcM_ip_slot { + int id; + void * pointer; + int next; +}; + +struct map_ip { + size_t array_size; + void ** array; + size_t hash_size; + struct _pbcM_ip_slot * slot; +}; + +struct _pbcM_si_slot { + const char *key; + size_t hash; + int id; + int next; +}; + +struct map_si { + size_t size; + struct _pbcM_si_slot slot[1]; +}; + +static size_t +calc_hash(const char *name) +{ + size_t len = strlen(name); + size_t h = len; + size_t step = (len>>5)+1; + size_t i; + for (i=len; i>=step; i-=step) + h = h ^ ((h<<5)+(h>>2)+(size_t)name[i-1]); + return h; +} + +struct map_si * +_pbcM_si_new(struct map_kv * table, int size) +{ + size_t sz = sizeof(struct map_si) + (size-1) * sizeof(struct _pbcM_si_slot); + struct map_si * ret = (struct map_si *)malloc(sz); + memset(ret,0,sz); + + ret->size = (size_t)size; + + int empty = 0; + int i; + + for (i=0;islot[hash]; + if (slot->key == NULL) { + slot->key = (const char *)table[i].pointer; + slot->id = table[i].id; + slot->hash = hash_full; + } else { + while(ret->slot[empty].key != NULL) { + ++empty; + } + struct _pbcM_si_slot * empty_slot = &ret->slot[empty]; + empty_slot->next = slot->next; + slot->next = empty + 1; + empty_slot->id = table[i].id; + empty_slot->key = (const char *)table[i].pointer; + empty_slot->hash = hash_full; + } + } + + return ret; +} + +void +_pbcM_si_delete(struct map_si *map) +{ + free(map); +} + +int +_pbcM_si_query(struct map_si *map, const char *key, int *result) +{ + size_t hash_full = calc_hash(key); + size_t hash = hash_full % map->size; + + struct _pbcM_si_slot * slot = &map->slot[hash]; + for (;;) { + if (slot->hash == hash_full && strcmp(slot->key, key) == 0) { + *result = slot->id; + return 0; + } + if (slot->next == 0) { + return 1; + } + slot = &map->slot[slot->next-1]; + } +} + +static struct map_ip * +_pbcM_ip_new_hash(struct map_kv * table, int size) +{ + struct map_ip * ret = (struct map_ip *)malloc(sizeof(struct map_ip)); + ret->array = NULL; + ret->array_size = 0; + ret->hash_size = (size_t)size; + ret->slot = (struct _pbcM_ip_slot *)malloc(sizeof(struct _pbcM_ip_slot) * size); + memset(ret->slot,0,sizeof(struct _pbcM_ip_slot) * size); + int empty = 0; + int i; + for (i=0;islot[hash]; + if (slot->pointer == NULL) { + slot->pointer = table[i].pointer; + slot->id = table[i].id; + } else { + while(ret->slot[empty].pointer != NULL) { + ++empty; + } + struct _pbcM_ip_slot * empty_slot = &ret->slot[empty]; + empty_slot->next = slot->next; + slot->next = empty + 1; + empty_slot->id = table[i].id; + empty_slot->pointer = table[i].pointer; + } + } + return ret; +} + +struct map_ip * +_pbcM_ip_new(struct map_kv * table, int size) +{ + int i; + int max = table[0].id; + if (max > size * 2 || max < 0) + return _pbcM_ip_new_hash(table,size); + for (i=1;i max) { + max = table[i].id; + if (max > size * 2) + return _pbcM_ip_new_hash(table,size); + } + } + struct map_ip * ret = (struct map_ip *)malloc(sizeof(struct map_ip)); + ret->hash_size = size; + ret->slot = NULL; + ret->array_size = max + 1; + ret->array = (void **)malloc((max+1) * sizeof(void *)); + memset(ret->array,0,(max+1) * sizeof(void *)); + for (i=0;iarray[table[i].id] = table[i].pointer; + } + return ret; +} + +void +_pbcM_ip_delete(struct map_ip * map) +{ + if (map) { + free(map->array); + free(map->slot); + free(map); + } +} + +static void +_inject(struct map_kv * table, struct map_ip *map) +{ + if (map->array) { + int n = 0; + int i; + for (i=0;i<(int)map->array_size;i++) { + if (map->array[i]) { + table[n].id = i; + table[n].pointer = map->array[i]; + ++ n; + } + } + } else { + int i; + for (i=0;i<(int)map->hash_size;i++) { + table[i].id = map->slot[i].id; + table[i].pointer = map->slot[i].pointer; + } + } +} + +struct map_ip * +_pbcM_ip_combine(struct map_ip *a, struct map_ip *b) +{ + int sz = (int)(a->hash_size + b->hash_size); + struct map_kv * table = (struct map_kv *)malloc(sz * sizeof(struct map_kv)); + memset(table , 0 , sz * sizeof(struct map_kv)); + _inject(table, a); + _inject(table + a->hash_size, b); + struct map_ip * r = _pbcM_ip_new(table, sz); + free(table); + return r; +} + +void * +_pbcM_ip_query(struct map_ip * map, int id) +{ + if (map == NULL) + return NULL; + if (map->array) { + if (id>=0 && id<(int)map->array_size) + return map->array[id]; + return NULL; + } + int hash = (unsigned)id % map->hash_size; + struct _pbcM_ip_slot * slot = &map->slot[hash]; + for (;;) { + if (slot->id == id) { + return slot->pointer; + } + if (slot->next == 0) { + return NULL; + } + slot = &map->slot[slot->next-1]; + } +} + +struct _pbcM_sp_slot { + const char *key; + size_t hash; + void *pointer; + int next; +}; + +struct map_sp { + size_t cap; + size_t size; + struct heap *heap; + struct _pbcM_sp_slot * slot; +}; + +struct map_sp * +_pbcM_sp_new(int max , struct heap *h) +{ + struct map_sp * ret = (struct map_sp *)HMALLOC(sizeof(struct map_sp)); + int cap = 1; + while (cap < max) { + cap *=2; + } + ret->cap = cap; + ret->size = 0; + ret->slot = (struct _pbcM_sp_slot *)HMALLOC(ret->cap * sizeof(struct _pbcM_sp_slot)); + memset(ret->slot,0,sizeof(struct _pbcM_sp_slot) * ret->cap); + ret->heap = h; + return ret; +} + +void +_pbcM_sp_delete(struct map_sp *map) +{ + if (map && map->heap == NULL) { + _pbcM_free(map->slot); + _pbcM_free(map); + } +} + +static void _pbcM_sp_rehash(struct map_sp *map); + +static void +_pbcM_sp_insert_hash(struct map_sp *map, const char *key, size_t hash_full, void * value) +{ + if (map->cap > map->size) { + size_t hash = hash_full & (map->cap-1); + struct _pbcM_sp_slot * slot = &map->slot[hash]; + if (slot->key == NULL) { + slot->key = key; + slot->pointer = value; + slot->hash = hash_full; + } else { + int empty = (hash + 1) & (map->cap-1); + while(map->slot[empty].key != NULL) { + empty = (empty + 1) & (map->cap-1); + } + struct _pbcM_sp_slot * empty_slot = &map->slot[empty]; + empty_slot->next = slot->next; + slot->next = empty + 1; + empty_slot->pointer = value; + empty_slot->key = key; + empty_slot->hash = hash_full; + } + map->size++; + return; + } + _pbcM_sp_rehash(map); + _pbcM_sp_insert_hash(map, key, hash_full, value); +} + +static void +_pbcM_sp_rehash(struct map_sp *map) { + struct heap * h = map->heap; + struct _pbcM_sp_slot * old_slot = map->slot; + size_t size = map->size; + map->size = 0; + map->cap *= 2; + map->slot = (struct _pbcM_sp_slot *)HMALLOC(sizeof(struct _pbcM_sp_slot)*map->cap); + memset(map->slot,0,sizeof(struct _pbcM_sp_slot)*map->cap); + size_t i; + for (i=0;icap-1); + struct _pbcM_sp_slot * slot = &map->slot[hash]; + if (slot->key == NULL) { + if (map->cap <= map->size) + goto _rehash; + slot->key = key; + slot->hash = hash_full; + map->size++; + return &(slot->pointer); + } else { + for (;;) { + if (slot->hash == hash_full && strcmp(slot->key, key) == 0) + return &(slot->pointer); + if (slot->next == 0) { + break; + } + slot = &map->slot[slot->next-1]; + } + if (map->cap <= map->size) + goto _rehash; + + int empty = (hash + 1) & (map->cap-1); + while(map->slot[empty].key != NULL) { + empty = (empty + 1) & (map->cap-1); + } + struct _pbcM_sp_slot * empty_slot = &map->slot[empty]; + empty_slot->next = slot->next; + slot->next = empty + 1; + empty_slot->key = key; + empty_slot->hash = hash_full; + + map->size++; + + return &(empty_slot->pointer); + } +_rehash: + _pbcM_sp_rehash(map); + return _pbcM_sp_query_insert_hash(map, key, hash_full); +} + +void +_pbcM_sp_insert(struct map_sp *map, const char *key, void * value) +{ + _pbcM_sp_insert_hash(map,key,calc_hash(key),value); +} + +void ** +_pbcM_sp_query_insert(struct map_sp *map, const char *key) +{ + return _pbcM_sp_query_insert_hash(map,key,calc_hash(key)); +} + +void * +_pbcM_sp_query(struct map_sp *map, const char *key) +{ + if (map == NULL) + return NULL; + size_t hash_full = calc_hash(key); + size_t hash = hash_full & (map->cap -1); + + struct _pbcM_sp_slot * slot = &map->slot[hash]; + for (;;) { + if (slot->hash == hash_full && strcmp(slot->key, key) == 0) { + return slot->pointer; + } + if (slot->next == 0) { + return NULL; + } + slot = &map->slot[slot->next-1]; + } +} + +void +_pbcM_sp_foreach(struct map_sp *map, void (*func)(void *p)) +{ + size_t i; + for (i=0;icap;i++) { + if (map->slot[i].pointer) { + func(map->slot[i].pointer); + } + } +} + +void +_pbcM_sp_foreach_ud(struct map_sp *map, void (*func)(void *p, void *ud), void *ud) +{ + size_t i; + for (i=0;icap;i++) { + if (map->slot[i].pointer) { + func(map->slot[i].pointer,ud); + } + } +} + +static int +_find_first(struct map_sp *map) +{ + size_t i; + for (i=0;icap;i++) { + if (map->slot[i].pointer) { + return i; + } + } + return -1; +} + +static int +_find_next(struct map_sp *map, const char *key) +{ + size_t hash_full = calc_hash(key); + size_t hash = hash_full & (map->cap -1); + + struct _pbcM_sp_slot * slot = &map->slot[hash]; + for (;;) { + if (slot->hash == hash_full && strcmp(slot->key, key) == 0) { + int i = slot - map->slot + 1; + while(icap) { + if (map->slot[i].pointer) { + return i; + } + ++i; + } + return -1; + } + if (slot->next == 0) { + return -1; + } + slot = &map->slot[slot->next-1]; + } +} + +void * +_pbcM_sp_next(struct map_sp *map, const char ** key) +{ + if (map == NULL) { + *key = NULL; + return NULL; + } + int idx; + if (*key == NULL) { + idx = _find_first(map); + } else { + idx = _find_next(map, *key); + } + if (idx < 0) { + *key = NULL; + return NULL; + } + *key = map->slot[idx].key; + return map->slot[idx].pointer; +} + + + diff --git a/luaclib/pb/src/map.h b/luaclib/pb/src/map.h new file mode 100644 index 0000000..d0fc6a9 --- /dev/null +++ b/luaclib/pb/src/map.h @@ -0,0 +1,33 @@ +#ifndef PROTOBUF_C_MAP_H +#define PROTOBUF_C_MAP_H + +#include "alloc.h" + +struct map_ip; +struct map_si; +struct map_sp; + +struct map_kv { + int id; + void *pointer; +}; + +struct map_si * _pbcM_si_new(struct map_kv * table, int size); +int _pbcM_si_query(struct map_si *map, const char *key, int *result); +void _pbcM_si_delete(struct map_si *map); + +struct map_ip * _pbcM_ip_new(struct map_kv * table, int size); +struct map_ip * _pbcM_ip_combine(struct map_ip * a, struct map_ip * b); +void * _pbcM_ip_query(struct map_ip * map, int id); +void _pbcM_ip_delete(struct map_ip *map); + +struct map_sp * _pbcM_sp_new(int max, struct heap *h); +void _pbcM_sp_insert(struct map_sp *map, const char *key, void * value); +void * _pbcM_sp_query(struct map_sp *map, const char *key); +void ** _pbcM_sp_query_insert(struct map_sp *map, const char *key); +void _pbcM_sp_delete(struct map_sp *map); +void _pbcM_sp_foreach(struct map_sp *map, void (*func)(void *p)); +void _pbcM_sp_foreach_ud(struct map_sp *map, void (*func)(void *p, void *ud), void *ud); +void * _pbcM_sp_next(struct map_sp *map, const char ** key); + +#endif diff --git a/luaclib/pb/src/pattern.c b/luaclib/pb/src/pattern.c new file mode 100644 index 0000000..b6d6829 --- /dev/null +++ b/luaclib/pb/src/pattern.c @@ -0,0 +1,1134 @@ +#include "alloc.h" +#include "context.h" +#include "varint.h" +#include "pattern.h" +#include "array.h" +#include "proto.h" +#include "map.h" + +#include +#ifndef _MSC_VER +#include +#endif +#include +#include +#include +#include + +static void +set_default_v(void * output, int ctype, pbc_var defv) { + switch (ctype) { + case CTYPE_INT32: + *(uint32_t *)output = defv->integer.low; + break; + case CTYPE_INT64: + *(uint64_t *)output = (uint64_t)defv->integer.low | (uint64_t)defv->integer.hi << 32; + break; + case CTYPE_DOUBLE: + *(double *)output = defv->real; + break; + case CTYPE_FLOAT: + *(float *)output = (float)defv->real; + break; + case CTYPE_BOOL: + *(bool *)output = (defv->integer.low != 0); + break; + case CTYPE_INT8: + *(uint8_t *)output = (uint8_t)defv->integer.low; + break; + case CTYPE_INT16: + *(uint16_t *)output = (uint16_t)defv->integer.low; + break; + case CTYPE_VAR: + *(union _pbc_var *)output = *defv; + break; + } +} + +static void +_pattern_set_default(struct _pattern_field *field, char *output) { + if (field->ctype == CTYPE_ARRAY || field->ctype == CTYPE_PACKED) { + struct _pbc_array *array = (struct _pbc_array *)(output + field->offset); + _pbcA_open(array); + } else if (field->ptype == PTYPE_ENUM) { + pbc_var defv; + defv->integer.low = field->defv->e.id; + defv->integer.hi = 0; + set_default_v(output + field->offset, field->ctype, defv); + } + set_default_v(output + field->offset, field->ctype, field->defv); +} + +void +pbc_pattern_set_default(struct pbc_pattern *pat, void *output) { + int i; + for (i=0;icount;i++) { + _pattern_set_default(&pat->f[i], (char *)output); + } +} + +// pattern unpack + +static struct _pattern_field * +bsearch_pattern(struct pbc_pattern *pat, int id) +{ + int begin = 0; + int end = pat->count; + while (begin < end) { + int mid = (begin + end)/2; + struct _pattern_field * f = &pat->f[mid]; + if (id == f->id) { + return f; + } + if (id < f->id) { + end = mid; + } else { + begin = mid + 1; + } + } + return NULL; +} + +static inline int +write_real(int ctype, double v, void *out) { + switch(ctype) { + case CTYPE_DOUBLE: + *(double *)out = v; + return 0; + case CTYPE_FLOAT: + *(float *)out = (float)v; + return 0; + case CTYPE_VAR: + ((union _pbc_var *)out)->real = v; + return 0; + } + return -1; +} + +static inline int +write_longlong(int ctype, struct longlong *i, void *out) { + switch(ctype) { + case CTYPE_INT32: + *(uint32_t *)out = i->low; + return 0; + case CTYPE_INT64: + *(uint64_t *)out = (uint64_t)i->low | (uint64_t)i->hi << 32; + return 0; + case CTYPE_BOOL: + *(bool *)out = (i->low !=0) ; + return 0; + case CTYPE_INT8: + *(uint8_t *)out = (uint8_t)i->low; + return 0; + case CTYPE_INT16: + *(uint8_t *)out = (uint16_t)i->low; + return 0; + case CTYPE_VAR: + ((union _pbc_var *)out)->integer = *i; + return 0; + } + return -1; +} + +static inline int +write_integer(int ctype, struct atom *a, void *out) { + return write_longlong(ctype, &(a->v.i), out); +} + +static int unpack_array(int ptype, char *buffer, struct atom *, pbc_array _array); + +int +_pbcP_unpack_packed(uint8_t *buffer, int size, int ptype, pbc_array array) { + pbc_var var; + var->integer.hi = 0; + int i; + switch(ptype) { + case PTYPE_DOUBLE: + if (size % 8 != 0) + return -1; + for (i=0;ireal = u.d; + _pbcA_push(array, var); + } + return size/8; + case PTYPE_FLOAT: + if (size % 4 != 0) + return -1; + for (i=0;ireal = (double)u.f; + _pbcA_push(array, var); + } + return size/4; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + if (size % 4 != 0) + return -1; + for (i=0;iinteger.low = (uint32_t)buffer[i] | + (uint32_t)buffer[i+1] << 8 | + (uint32_t)buffer[i+2] << 16 | + (uint32_t)buffer[i+3] << 24; + _pbcA_push(array, var); + } + return size/4; + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + if (size % 8 != 0) + return -1; + for (i=0;iinteger.low = (uint32_t)buffer[i] | + (uint32_t)buffer[i+1] << 8 | + (uint32_t)buffer[i+2] << 16 | + (uint32_t)buffer[i+3] << 24; + var->integer.hi = (uint32_t)buffer[i+4] | + (uint32_t)buffer[i+5] << 8 | + (uint32_t)buffer[i+6] << 16 | + (uint32_t)buffer[i+7] << 24; + _pbcA_push(array, var); + } + return size/8; + case PTYPE_INT64: + case PTYPE_UINT64: + case PTYPE_INT32: + case PTYPE_UINT32: + case PTYPE_ENUM: // enum must be integer type in pattern mode + case PTYPE_BOOL: { + int n = 0; + while (size > 0) { + int len; + if (size >= 10) { + len = _pbcV_decode(buffer, &(var->integer)); + } else { + uint8_t temp[10]; + memcpy(temp, buffer, size); + len = _pbcV_decode(buffer, &(var->integer)); + if (len > size) + return -1; + } + _pbcA_push(array, var); + buffer += len; + size -= len; + ++n; + } + return n; + } + case PTYPE_SINT32: { + int n = 0; + while (size > 0) { + int len; + if (size >= 10) { + len = _pbcV_decode(buffer, &(var->integer)); + _pbcV_dezigzag32(&(var->integer)); + } else { + uint8_t temp[10]; + memcpy(temp, buffer, size); + len = _pbcV_decode(buffer, &(var->integer)); + if (len > size) + return -1; + _pbcV_dezigzag32(&(var->integer)); + } + _pbcA_push(array, var); + buffer += len; + size -= len; + ++n; + } + return n; + } + case PTYPE_SINT64: { + int n = 0; + while (size > 0) { + int len; + if (size >= 10) { + len = _pbcV_decode(buffer, &(var->integer)); + _pbcV_dezigzag64(&(var->integer)); + } else { + uint8_t temp[10]; + memcpy(temp, buffer, size); + len = _pbcV_decode(buffer, &(var->integer)); + if (len > size) + return -1; + _pbcV_dezigzag64(&(var->integer)); + } + _pbcA_push(array, var); + buffer += len; + size -= len; + ++n; + } + return n; + } + } + return -1; +} + +static int +unpack_field(int ctype, int ptype, char * buffer, struct atom * a, void *out) { + if (ctype == CTYPE_ARRAY) { + return unpack_array(ptype, buffer, a , (struct _pbc_array *)out); + } + if (ctype == CTYPE_PACKED) { + return _pbcP_unpack_packed((uint8_t *)buffer + a->v.s.start, a->v.s.end - a->v.s.start, ptype, (struct _pbc_array *)out); + } + switch(ptype) { + case PTYPE_DOUBLE: + CHECK_BIT64(a, -1); + return write_real(ctype, read_double(a), out); + case PTYPE_FLOAT: + CHECK_BIT32(a, -1); + return write_real(ctype, read_float(a), out); + case PTYPE_INT64: + case PTYPE_UINT64: + case PTYPE_INT32: + case PTYPE_UINT32: + case PTYPE_ENUM: // enum must be integer type in pattern mode + case PTYPE_BOOL: + CHECK_VARINT(a, -1); + return write_integer(ctype, a , out); + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + CHECK_BIT32(a, -1); + return write_integer(ctype, a , out); + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + CHECK_BIT64(a, -1); + return write_integer(ctype, a , out); + case PTYPE_SINT32: { + CHECK_VARINT(a, -1); + struct longlong temp = a->v.i; + _pbcV_dezigzag32(&temp); + return write_longlong(ctype, &temp , out); + } + case PTYPE_SINT64: { + CHECK_LEND(a, -1); + struct longlong temp = a->v.i; + _pbcV_dezigzag64(&temp); + return write_longlong(ctype, &temp , out); + } + case PTYPE_MESSAGE: + CHECK_LEND(a, -1); + ((union _pbc_var *)out)->m.buffer = buffer + a->v.s.start; + ((union _pbc_var *)out)->m.len = a->v.s.end - a->v.s.start; + return 0; + case PTYPE_STRING: + case PTYPE_BYTES: + CHECK_LEND(a, -1); + ((struct pbc_slice *)out)->buffer = buffer + a->v.s.start; + ((struct pbc_slice *)out)->len = a->v.s.end - a->v.s.start; + return 0; + } + return -1; +} + +static int +unpack_array(int ptype, char *buffer, struct atom * a, pbc_array _array) { + pbc_var var; + int r = unpack_field(CTYPE_VAR, ptype, buffer, a , var); + if (r !=0 ) + return r; + _pbcA_push(_array , var); + + return 0; +} + +void +pbc_pattern_close_arrays(struct pbc_pattern *pat, void * data) { + int i; + for (i=0;icount;i++) { + if (pat->f[i].ctype == CTYPE_ARRAY || pat->f[i].ctype == CTYPE_PACKED) { + void *array = (char *)data + pat->f[i].offset; + _pbcA_close((struct _pbc_array *)array); + } + } +} + +static inline int +_pack_wiretype(uint32_t wt, struct pbc_slice *s) { + int len; + if (s->len < 10) { + uint8_t temp[10]; + len = _pbcV_encode32(wt, temp); + if (len > s->len) + return -1; + memcpy(s->buffer, temp, len); + } else { + len = _pbcV_encode32(wt, (uint8_t *)s->buffer); + } + s->buffer = (char *)s->buffer + len; + s->len -= len; + return len; +} + +static inline int +_pack_varint64(uint64_t i64, struct pbc_slice *s) { + int len; + if (s->len < 10) { + uint8_t temp[10]; + len = _pbcV_encode(i64, temp); + if (len > s->len) + return -1; + memcpy(s->buffer, temp, len); + } else { + len = _pbcV_encode(i64, (uint8_t *)s->buffer); + } + s->buffer = (char *)s->buffer + len; + s->len -= len; + return len; +} + +static inline int +_pack_sint32(uint32_t v, struct pbc_slice *s) { + int len; + if (s->len < 10) { + uint8_t temp[10]; + len = _pbcV_zigzag32(v, temp); + if (len > s->len) + return -1; + memcpy(s->buffer, temp, len); + } else { + len = _pbcV_zigzag32(v, (uint8_t *)s->buffer); + } + s->buffer = (char *)s->buffer + len; + s->len -= len; + return len; +} + +static inline int +_pack_sint64(uint64_t v, struct pbc_slice *s) { + int len; + if (s->len < 10) { + uint8_t temp[10]; + len = _pbcV_zigzag(v, temp); + if (len > s->len) + return -1; + memcpy(s->buffer, temp, len); + } else { + len = _pbcV_zigzag(v, (uint8_t *)s->buffer); + } + s->buffer = (char *)s->buffer + len; + s->len -= len; + return len; +} + +static inline void +_fix32_encode(uint32_t v , uint8_t *buffer) { + buffer[0] = (uint8_t) v; + buffer[1] = (uint8_t) (v >> 8); + buffer[2] = (uint8_t) (v >> 16); + buffer[3] = (uint8_t) (v >> 24); +} + +static inline void +_fix64_encode(struct longlong *v , uint8_t *buffer) { + _fix32_encode(v->low , buffer); + _fix32_encode(v->hi, buffer + 4); +} + +static int +_pack_number(int ptype , int ctype , struct pbc_slice *s, void *input) { + pbc_var var; + if (ctype == CTYPE_VAR) { + memcpy(var, input, sizeof(var)); + } else { + switch (ctype) { + case CTYPE_INT32: + var->integer.low = *(uint32_t *)input; + var->integer.hi = 0; + break; + case CTYPE_INT64: { + uint64_t v = *(uint64_t *)input; + var->integer.low = (uint32_t) (v & 0xffffffff); + var->integer.hi = (uint32_t) (v >> 32); + break; + } + case CTYPE_INT16: + var->integer.low = *(uint16_t *)input; + var->integer.hi = 0; + break; + case CTYPE_INT8: + var->integer.low = *(uint8_t *)input; + var->integer.hi = 0; + break; + case CTYPE_BOOL: + var->integer.low = *(bool *)input; + var->integer.hi = 0; + break; + case CTYPE_DOUBLE: + var->real = *(double *)input; + break; + case CTYPE_FLOAT: + var->real = *(float *)input; + break; + } + } + + switch(ptype) { + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + if (s->len < 8) + return -1; + _fix64_encode(&(var->integer), (uint8_t *)s->buffer); + s->buffer = (char *)s->buffer + 8; + s->len -= 8; + return 8; + case PTYPE_DOUBLE: + if (s->len < 8) + return -1; + double_encode(var->real , (uint8_t *)s->buffer); + s->buffer = (char *)s->buffer + 8; + s->len -= 8; + return 8; + case PTYPE_FLOAT: + if (s->len < 4) + return -1; + float_encode((float)var->real , (uint8_t *)s->buffer); + s->buffer = (char *)s->buffer + 4; + s->len -= 4; + return 4; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + if (s->len < 4) + return -1; + _fix32_encode(var->integer.low, (uint8_t *)s->buffer); + s->buffer = (char *)s->buffer + 4; + s->len -= 4; + return 4; + case PTYPE_UINT64: + case PTYPE_INT64: + case PTYPE_INT32: + return _pack_varint64((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32, s); + case PTYPE_UINT32: + case PTYPE_BOOL: + case PTYPE_ENUM: + return _pack_wiretype(var->integer.low , s); + case PTYPE_SINT32: + return _pack_sint32(var->integer.low , s); + case PTYPE_SINT64: + return _pack_sint64((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32 , s); + default: + return -1; + } +} + +static int +_pack_field(struct _pattern_field *pf , int ctype, struct pbc_slice *s, void *input) { + int wiretype; + int ret = 0; + int len; + struct pbc_slice * input_slice; + struct pbc_slice string_slice; + + switch(pf->ptype) { + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + case PTYPE_DOUBLE: + wiretype = WT_BIT64; + goto _number; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + case PTYPE_FLOAT: + wiretype = WT_BIT32; + goto _number; + case PTYPE_UINT64: + case PTYPE_INT64: + case PTYPE_INT32: + case PTYPE_BOOL: + case PTYPE_UINT32: + case PTYPE_ENUM: + case PTYPE_SINT32: + case PTYPE_SINT64: + wiretype = WT_VARINT; + goto _number; + case PTYPE_STRING: + wiretype = WT_LEND; + input_slice = (struct pbc_slice *)input; + if (input_slice->len >= 0) + goto _string; + string_slice.buffer = input_slice->buffer; + string_slice.len = strlen((const char *)string_slice.buffer) - input_slice->len; + input_slice = &string_slice; + + goto _string; + case PTYPE_MESSAGE: + case PTYPE_BYTES: + wiretype = WT_LEND; + goto _bytes; + default: + break; + } + + return 0; +_bytes: + input_slice = (struct pbc_slice *)input; +_string: + len = _pack_wiretype(pf->id << 3 | WT_LEND , s); + if (len < 0) { + return len; + } + ret += len; + len = _pack_wiretype(input_slice->len , s); + if (len < 0) { + return len; + } + ret += len; + if (input_slice->len > s->len) + return -1; + memcpy(s->buffer , input_slice->buffer, input_slice->len); + ret += input_slice->len; + s->buffer = (char *)s->buffer + input_slice->len; + s->len -= input_slice->len; + + return ret; +_number: + len = _pack_wiretype(pf->id << 3 | wiretype , s); + if (len < 0) { + return len; + } + ret += len; + len = _pack_number(pf->ptype, ctype , s, input); + if (len < 0) { + return len; + } + ret += len; + + return ret; +} + +static int +_pack_repeated(struct _pattern_field *pf , struct pbc_slice *s, pbc_array array) { + int n = pbc_array_size(array); + int ret = 0; + if (n>0) { + int i; + for (i=0;ilen - len < n * width) + return -1; + int i; + for (i=0;iptype, CTYPE_VAR , s, _pbcA_index_p(array, i)); + } + + return len + n * width; +} + +static int +_pack_packed_varint(struct _pattern_field *pf , struct pbc_slice *slice, pbc_array array) { + struct pbc_slice s = * slice; + int n = pbc_array_size(array); + int estimate = n; + int estimate_len = _pack_wiretype(estimate , &s); + if (estimate_len < 0) { + return -1; + } + int i; + int packed_len = 0; + for (i=0;iptype, CTYPE_VAR , &s, _pbcA_index_p(array, i)); + if (len < 0) + return -1; + packed_len += len; + } + if (packed_len == estimate) { + *slice = s; + return packed_len + estimate_len; + } + uint8_t temp[10]; + struct pbc_slice header_slice = { temp , 10 }; + int header_len = _pack_wiretype(packed_len , &header_slice); + if (header_len == estimate_len) { + memcpy(slice->buffer , temp , header_len); + *slice = s; + return packed_len + estimate_len; + } + if (header_len + packed_len > slice->len) + return -1; + memmove((char *)slice->buffer + header_len , (char *)slice->buffer + estimate_len, packed_len); + memcpy(slice->buffer , temp , header_len); + slice->buffer = (char *)slice->buffer + packed_len + header_len; + slice->len -= packed_len + header_len; + return packed_len + header_len; +} + +static int +_pack_packed(struct _pattern_field *pf , struct pbc_slice *s, pbc_array array) { + int n = pbc_array_size(array); + if (n == 0) + return 0; + + int ret = 0; + int len; + len = _pack_wiretype(pf->id << 3 | WT_LEND , s); + if (len < 0) { + return len; + } + ret += len; + + switch (pf->ptype) { + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + case PTYPE_DOUBLE: + len = _pack_packed_fixed(pf, 8, s , array); + if (len < 0) + return len; + break; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + case PTYPE_FLOAT: + len = _pack_packed_fixed(pf, 4, s , array); + if (len < 0) + return len; + break; + case PTYPE_UINT64: + case PTYPE_INT64: + case PTYPE_INT32: + case PTYPE_BOOL: + case PTYPE_UINT32: + case PTYPE_ENUM: + case PTYPE_SINT32: + case PTYPE_SINT64: + len = _pack_packed_varint(pf, s, array); + if (len < 0) + return len; + break; + } + ret += len; + + return ret; +} + +static bool +_is_default(struct _pattern_field * pf, void * in) { + switch (pf->ctype) { + case CTYPE_INT64: { + struct longlong * d64 = &pf->defv->integer; + return ((uint64_t)d64->low | (uint64_t)d64->hi << 32) == *(uint64_t *)in; + } + case CTYPE_DOUBLE: + return pf->defv->real == *(double *)in; + case CTYPE_FLOAT: + return (float)(pf->defv->real) == *(float *)in; + case CTYPE_INT32: + return pf->defv->integer.low == *(uint32_t *)in; + case CTYPE_INT16: + return (uint16_t)(pf->defv->integer.low) == *(uint16_t *)in; + case CTYPE_INT8: + return (uint8_t)(pf->defv->integer.low) == *(uint8_t *)in; + case CTYPE_BOOL: + if (pf->defv->integer.low) + return *(bool *)in == true; + else + return *(bool *)in == false; + } + if (pf->ptype == PTYPE_STRING) { + struct pbc_slice *slice = (struct pbc_slice *)in; + if (slice->buffer == NULL) { + return pf->defv->s.str[0] == '\0'; + } + int len = slice->len; + if (len <= 0) { + return strcmp(pf->defv->s.str, (const char *)slice->buffer) == 0; + } + return len == pf->defv->s.len && memcmp(pf->defv->s.str, slice->buffer, len)==0; + } else if (pf->ptype == PTYPE_BYTES) { + struct pbc_slice *slice = (struct pbc_slice *)in; + if (slice->buffer == NULL) + return true; + } + + return false; +} + +int +pbc_pattern_pack(struct pbc_pattern *pat, void *input, struct pbc_slice * s) +{ + struct pbc_slice slice = *s; + int i; + for (i=0;icount;i++) { + struct _pattern_field * pf = &pat->f[i]; + void * in = (char *)input + pf->offset; + int len = 0; + switch(pf->label) { + case LABEL_OPTIONAL: + if (_is_default(pf , in)) { + break; + } + case LABEL_REQUIRED: + len = _pack_field(pf, pf->ctype, &slice, in); + break; + case LABEL_REPEATED: + len = _pack_repeated(pf, &slice , (struct _pbc_array *)in); + break; + case LABEL_PACKED: + len = _pack_packed(pf, &slice , (struct _pbc_array *)in); + break; + } + if (len < 0) { + return len; + } + } + int len = (char *)slice.buffer - (char *)s->buffer; + int ret = s->len - len; + s->len = len; + return ret; +} + +int +pbc_pattern_unpack(struct pbc_pattern *pat, struct pbc_slice *s, void * output) { + if (s->len == 0) { + pbc_pattern_set_default(pat, output); + return 0; + } + pbc_ctx _ctx; + int r = _pbcC_open(_ctx, s->buffer, s->len); + if (r <= 0) { + pat->env->lasterror = "Pattern unpack open context error"; + _pbcC_close(_ctx); + return r-1; + } + + struct context * ctx = (struct context *)_ctx; + bool * field = (bool *)alloca(pat->count * sizeof(bool)); + memset(field, 0, pat->count * sizeof(bool)); + + int i; + int fc = 0; + + for (i=0;inumber;i++) { + struct _pattern_field * f = bsearch_pattern(pat, ctx->a[i].wire_id >> 3); + if (f) { + int index = f - pat->f; + if (field[index] == false) { + field[index] = true; + ++fc; + if ((f->ctype == CTYPE_ARRAY || f->ctype == CTYPE_PACKED)) { + struct _pbc_array *array = (struct _pbc_array *)((char *)output + f->offset); + _pbcA_open(array); + } + } + char * out = (char *)output + f->offset; + if (unpack_field(f->ctype , f->ptype , ctx->buffer , &ctx->a[i], out) < 0) { + int j; + for (j=0;jcount;j++) { + if (field[j] == true && (pat->f[j].ctype == CTYPE_ARRAY || pat->f[j].ctype == CTYPE_PACKED)) { + void *array = (char *)output + pat->f[j].offset; + _pbcA_close((struct _pbc_array *)array); + } + } + _pbcC_close(_ctx); + pat->env->lasterror = "Pattern unpack field error"; + return -i-1; + } + } + } + _pbcC_close(_ctx); + if (fc != pat->count) { + for (i=0;icount;i++) { + if (field[i] == false) { + _pattern_set_default(&pat->f[i], (char *)output); + } + } + } + return 0; +} + +/* + format : key %type + %f float + %F double + %d int32 + %D int64 + %b bool + %h int16 + %c int8 + %s slice + %a array +*/ + +static int +_ctype(const char * ctype) { + if (ctype[0]!='%') + return -1; + switch (ctype[1]) { + case 'f': + return CTYPE_FLOAT; + case 'F': + return CTYPE_DOUBLE; + case 'd': + return CTYPE_INT32; + case 'D': + return CTYPE_INT64; + case 'b': + return CTYPE_BOOL; + case 'h': + return CTYPE_INT16; + case 'c': + return CTYPE_INT8; + case 's': + return CTYPE_VAR; + case 'a': + return CTYPE_ARRAY; + default: + return -1; + } +} + +static int +_ctype_size(const char *ctype) { + switch (ctype[1]) { + case 'f': + return sizeof(float); + case 'F': + return sizeof(double); + case 'd': + return sizeof(int32_t); + case 'D': + return sizeof(int64_t); + case 'b': + return sizeof(bool); + case 'h': + return sizeof(int16_t); + case 'c': + return sizeof(int8_t); + case 's': + return sizeof(struct pbc_slice); + case 'a': + return sizeof(pbc_array); + default: + return 0; + } +} + +static const char * +_copy_string(const char *format , char ** temp) { + char * output = *temp; + while (*format == ' ' || *format == '\t' || *format == '\n' || *format == '\r') { + ++format; + } + while (*format != '\0' && + *format != ' ' && + *format != '\t' && + *format != '\n' && + *format != '\r') { + *output = *format; + ++output; + ++format; + } + *output = '\0'; + ++output; + *temp = output; + + return format; +} + +static int +_scan_pattern(const char * format , char * temp) { + int n = 0; + for(;;) { + format = _copy_string(format , &temp); + if (format[0] == '\0') + return 0; + ++n; + format = _copy_string(format , &temp); + if (format[0] == '\0') + return n; + } +} + +static int +_comp_field(const void * a, const void * b) { + const struct _pattern_field * fa = (const struct _pattern_field *)a; + const struct _pattern_field * fb = (const struct _pattern_field *)b; + + return fa->id - fb->id; +} + +struct pbc_pattern * +_pbcP_new(struct pbc_env * env, int n) { + size_t sz = sizeof(struct pbc_pattern) + (sizeof(struct _pattern_field)) * (n-1); + struct pbc_pattern * ret = (struct pbc_pattern *)malloc(sz); + memset(ret, 0 , sz); + ret->count = n; + ret->env = env; + return ret; +} + +static int +_check_ctype(struct _field * field, struct _pattern_field *f) { + if (field->label == LABEL_REPEATED) { + return f->ctype != CTYPE_ARRAY; + } + if (field->label == LABEL_PACKED) { + return f->ctype != CTYPE_PACKED; + } + if (field->type == PTYPE_STRING || field->type == PTYPE_MESSAGE || field->type == PTYPE_BYTES) { + return f->ctype != CTYPE_VAR; + } + if (field->type == PTYPE_FLOAT || field->type == PTYPE_DOUBLE) { + return !(f->ctype == CTYPE_DOUBLE || f->ctype == CTYPE_FLOAT); + } + if (field->type == PTYPE_ENUM) { + return !(f->ctype == CTYPE_INT8 || + f->ctype == CTYPE_INT8 || + f->ctype == CTYPE_INT16 || + f->ctype == CTYPE_INT32 || + f->ctype == CTYPE_INT64); + } + + return f->ctype == CTYPE_VAR || f->ctype == CTYPE_ARRAY || f->ctype == CTYPE_PACKED || + f->ctype == CTYPE_DOUBLE || f->ctype == CTYPE_FLOAT; +} + +struct pbc_pattern * +_pattern_new(struct _message *m, const char *format) { + int len = strlen(format); + char * temp = (char *)alloca(len+1); + int n = _scan_pattern(format, temp); + struct pbc_pattern * pat = _pbcP_new(m->env, n); + int i; + + const char *ptr = temp; + + int offset = 0; + + for (i=0;if[i]); + struct _field * field = (struct _field *)_pbcM_sp_query(m->name, ptr); + if (field == NULL) { + m->env->lasterror = "Pattern @new query none exist field"; + goto _error; + } + f->id = field->id; + f->ptype = field->type; + *f->defv = *field->default_v; + f->offset = offset; + f->label = field->label; + ptr += strlen(ptr) + 1; + f->ctype = _ctype(ptr); + if (f->ctype < 0) { + m->env->lasterror = "Pattern @new use an invalid ctype"; + goto _error; + } + + if (f->ctype == CTYPE_ARRAY && field->label == LABEL_PACKED) { + f->ctype = CTYPE_PACKED; + } + if (_check_ctype(field, f)) { + m->env->lasterror = "Pattern @new ctype check error"; + goto _error; + } + + offset += _ctype_size(ptr); + ptr += strlen(ptr) + 1; + } + + pat->count = n; + + qsort(pat->f , n , sizeof(struct _pattern_field), _comp_field); + return pat; +_error: + free(pat); + return NULL; +} + +struct pbc_pattern * +pbc_pattern_new(struct pbc_env * env , const char * message, const char * format, ... ) { + struct _message *m = _pbcP_get_message(env, message); + if (m==NULL) { + env->lasterror = "Pattern new can't find proto"; + return NULL; + } + if (format[0]=='@') { + return _pattern_new(m , format+1); + } + + int len = strlen(format); + char * temp = (char *)alloca(len+1); + int n = _scan_pattern(format, temp); + struct pbc_pattern * pat = _pbcP_new(env, n); + int i; + va_list ap; + va_start(ap , format); + + const char *ptr = temp; + + for (i=0;if[i]); + struct _field * field = (struct _field *)_pbcM_sp_query(m->name, ptr); + if (field == NULL) { + env->lasterror = "Pattern new query none exist field"; + goto _error; + } + f->id = field->id; + f->ptype = field->type; + *f->defv = *field->default_v; + f->offset = va_arg(ap, int); + f->label = field->label; + + ptr += strlen(ptr) + 1; + + f->ctype = _ctype(ptr); + if (f->ctype < 0) { + env->lasterror = "Pattern new use an invalid ctype"; + goto _error; + } + if (f->ctype == CTYPE_ARRAY && field->label == LABEL_PACKED) { + f->ctype = CTYPE_PACKED; + } + if (_check_ctype(field, f)) { + env->lasterror = "Pattern new ctype check error"; + goto _error; + } + + ptr += strlen(ptr) + 1; + } + + va_end(ap); + + pat->count = n; + + qsort(pat->f , n , sizeof(struct _pattern_field), _comp_field); + return pat; +_error: + free(pat); + return NULL; +} + +void +pbc_pattern_delete(struct pbc_pattern * pat) { + free(pat); +} diff --git a/luaclib/pb/src/pattern.h b/luaclib/pb/src/pattern.h new file mode 100644 index 0000000..880d3d5 --- /dev/null +++ b/luaclib/pb/src/pattern.h @@ -0,0 +1,26 @@ +#ifndef PROTOBUF_C_PATTERN_H +#define PROTOBUF_C_PATTERN_H + +#include "pbc.h" +#include "context.h" +#include "array.h" + +struct _pattern_field { + int id; + int offset; + int ptype; + int ctype; + int label; + pbc_var defv; +}; + +struct pbc_pattern { + struct pbc_env * env; + int count; + struct _pattern_field f[1]; +}; + +struct pbc_pattern * _pbcP_new(struct pbc_env * env, int n); +int _pbcP_unpack_packed(uint8_t *buffer, int size, int ptype, pbc_array array); + +#endif diff --git a/luaclib/pb/src/pbc.h b/luaclib/pb/src/pbc.h new file mode 100644 index 0000000..1ccc487 --- /dev/null +++ b/luaclib/pb/src/pbc.h @@ -0,0 +1,104 @@ +#ifndef PROTOBUF_C_H +#define PROTOBUF_C_H + +#include +#include + +#define PBC_ARRAY_CAP 64 + +#define PBC_NOEXIST -1 +#define PBC_INT 1 +#define PBC_REAL 2 +#define PBC_BOOL 3 +#define PBC_ENUM 4 +#define PBC_STRING 5 +#define PBC_MESSAGE 6 +#define PBC_FIXED64 7 +#define PBC_FIXED32 8 +#define PBC_BYTES 9 +#define PBC_INT64 10 +#define PBC_UINT 11 +#define PBC_UNKNOWN 12 +#define PBC_REPEATED 128 + +typedef struct _pbc_array { char _data[PBC_ARRAY_CAP]; } pbc_array[1]; + +struct pbc_slice { + void *buffer; + int len; +}; + +struct pbc_pattern; +struct pbc_env; +struct pbc_rmessage; +struct pbc_wmessage; + +struct pbc_env * pbc_new(void); +void pbc_delete(struct pbc_env *); +int pbc_register(struct pbc_env *, struct pbc_slice * slice); +int pbc_type(struct pbc_env *, const char * type_name , const char * key , const char ** type); +const char * pbc_error(struct pbc_env *); + +// callback api +union pbc_value { + struct { + uint32_t low; + uint32_t hi; + } i; + double f; + struct pbc_slice s; + struct { + int id; + const char * name; + } e; +}; + +typedef void (*pbc_decoder)(void *ud, int type, const char * type_name, union pbc_value *v, int id, const char *key); +int pbc_decode(struct pbc_env * env, const char * type_name , struct pbc_slice * slice, pbc_decoder f, void *ud); + +// message api + +struct pbc_rmessage * pbc_rmessage_new(struct pbc_env * env, const char * type_name , struct pbc_slice * slice); +void pbc_rmessage_delete(struct pbc_rmessage *); + +uint32_t pbc_rmessage_integer(struct pbc_rmessage * , const char *key , int index, uint32_t *hi); +double pbc_rmessage_real(struct pbc_rmessage * , const char *key , int index); +const char * pbc_rmessage_string(struct pbc_rmessage * , const char *key , int index, int *sz); +struct pbc_rmessage * pbc_rmessage_message(struct pbc_rmessage *, const char *key, int index); +int pbc_rmessage_size(struct pbc_rmessage *, const char *key); +int pbc_rmessage_next(struct pbc_rmessage *, const char **key); + +struct pbc_wmessage * pbc_wmessage_new(struct pbc_env * env, const char *type_name); +void pbc_wmessage_delete(struct pbc_wmessage *); + +// for negative integer, pass -1 to hi +int pbc_wmessage_integer(struct pbc_wmessage *, const char *key, uint32_t low, uint32_t hi); +int pbc_wmessage_real(struct pbc_wmessage *, const char *key, double v); +int pbc_wmessage_string(struct pbc_wmessage *, const char *key, const char * v, int len); +struct pbc_wmessage * pbc_wmessage_message(struct pbc_wmessage *, const char *key); +void * pbc_wmessage_buffer(struct pbc_wmessage *, struct pbc_slice * slice); + +// array api + +int pbc_array_size(pbc_array); +uint32_t pbc_array_integer(pbc_array array, int index, uint32_t *hi); +double pbc_array_real(pbc_array array, int index); +struct pbc_slice * pbc_array_slice(pbc_array array, int index); + +void pbc_array_push_integer(pbc_array array, uint32_t low, uint32_t hi); +void pbc_array_push_slice(pbc_array array, struct pbc_slice *); +void pbc_array_push_real(pbc_array array, double v); + +struct pbc_pattern * pbc_pattern_new(struct pbc_env * , const char * message, const char *format, ...); +void pbc_pattern_delete(struct pbc_pattern *); + +// return unused bytes , -1 for error +int pbc_pattern_pack(struct pbc_pattern *, void *input, struct pbc_slice * s); + +// <0 for error +int pbc_pattern_unpack(struct pbc_pattern *, struct pbc_slice * s , void * output); + +void pbc_pattern_set_default(struct pbc_pattern * , void *data); +void pbc_pattern_close_arrays(struct pbc_pattern *, void *data); + +#endif diff --git a/luaclib/pb/src/proto.c b/luaclib/pb/src/proto.c new file mode 100644 index 0000000..265b6aa --- /dev/null +++ b/luaclib/pb/src/proto.c @@ -0,0 +1,252 @@ +#include "pbc.h" +#include "proto.h" +#include "pattern.h" +#include "map.h" +#include "alloc.h" +#include "stringpool.h" +#include "bootstrap.h" + +#include +#include + +const char * +pbc_error(struct pbc_env * p) { + const char *err = p->lasterror; + p->lasterror = ""; + return err; +} + +struct _message * +_pbcP_get_message(struct pbc_env * p , const char *name) { + return (struct _message *)_pbcM_sp_query(p->msgs, name); +} + +struct pbc_env * +pbc_new(void) { + struct pbc_env * p = (struct pbc_env *)malloc(sizeof(*p)); + p->files = _pbcM_sp_new(0 , NULL); + p->enums = _pbcM_sp_new(0 , NULL); + p->msgs = _pbcM_sp_new(0 , NULL); + p->lasterror = ""; + + _pbcB_init(p); + + return p; +} + +static void +free_enum(void *p) { + struct _enum * e = (struct _enum *)p; + _pbcM_ip_delete(e->id); + _pbcM_si_delete(e->name); + + free(p); +} + +static void +free_stringpool(void *p) { + _pbcS_delete((struct _stringpool *)p); +} + +static void +free_msg(void *p) { + struct _message * m = (struct _message *)p; + if (m->id) + _pbcM_ip_delete(m->id); + free(m->def); + _pbcM_sp_foreach(m->name, free); + _pbcM_sp_delete(m->name); + free(p); +} + +void +pbc_delete(struct pbc_env *p) { + _pbcM_sp_foreach(p->enums, free_enum); + _pbcM_sp_delete(p->enums); + + _pbcM_sp_foreach(p->msgs, free_msg); + _pbcM_sp_delete(p->msgs); + + _pbcM_sp_foreach(p->files, free_stringpool); + _pbcM_sp_delete(p->files); + + free(p); +} + +struct _enum * +_pbcP_push_enum(struct pbc_env * p, const char *name, struct map_kv *table, int sz) { + void * check = _pbcM_sp_query(p->enums, name); + if (check) + return NULL; + struct _enum * v = (struct _enum *)malloc(sizeof(*v)); + v->key = name; + v->id = _pbcM_ip_new(table,sz); + v->name = _pbcM_si_new(table,sz); + v->default_v->e.id = table[0].id; + v->default_v->e.name = (const char *)table[0].pointer; + + _pbcM_sp_insert(p->enums, name , v); + return v; +} + +void +_pbcP_push_message(struct pbc_env * p, const char *name, struct _field *f , pbc_array queue) { + struct _message * m = (struct _message *)_pbcM_sp_query(p->msgs, name); + if (m==NULL) { + m = (struct _message *)malloc(sizeof(*m)); + m->def = NULL; + m->key = name; + m->id = NULL; + m->name = _pbcM_sp_new(0 , NULL); + m->env = p; + _pbcM_sp_insert(p->msgs, name, m); + } + struct _field * field = (struct _field *)malloc(sizeof(*field)); + memcpy(field,f,sizeof(*f)); + _pbcM_sp_insert(m->name, field->name, field); + pbc_var atom; + atom->m.buffer = field; + if (f->type == PTYPE_MESSAGE || f->type == PTYPE_ENUM) { + _pbcA_push(queue, atom); + } +} + +struct _iter { + int count; + struct map_kv * table; +}; + +static void +_count(void *p, void *ud) { + struct _iter *iter = (struct _iter *)ud; + iter->count ++; +} + +static void +_set_table(void *p, void *ud) { + struct _field * field = (struct _field *)p; + struct _iter *iter = (struct _iter *)ud; + iter->table[iter->count].id = field->id; + iter->table[iter->count].pointer = field; + ++iter->count; +} + +struct _message * +_pbcP_init_message(struct pbc_env * p, const char *name) { + struct _message * m = (struct _message *)_pbcM_sp_query(p->msgs, name); + if (m == NULL) { + m = (struct _message *)malloc(sizeof(*m)); + m->def = NULL; + m->key = name; + m->id = NULL; + m->name = _pbcM_sp_new(0 , NULL); + m->env = p; + _pbcM_sp_insert(p->msgs, name, m); + + return m; + } + if (m->id) { + // extend message, delete old id map. + _pbcM_ip_delete(m->id); + } + struct _iter iter = { 0, NULL }; + _pbcM_sp_foreach_ud(m->name, _count, &iter); + iter.table = (struct map_kv *)malloc(iter.count * sizeof(struct map_kv)); + iter.count = 0; + _pbcM_sp_foreach_ud(m->name, _set_table, &iter); + + m->id = _pbcM_ip_new(iter.table , iter.count); + + free(iter.table); + + return m; +} + +int +_pbcP_message_default(struct _message * m, const char * name, pbc_var defv) { + struct _field * f= (struct _field *)_pbcM_sp_query(m->name, name); + if (f==NULL) { + // invalid key + defv->p[0] = NULL; + defv->p[1] = NULL; + return -1; + } + *defv = *(f->default_v); + return f->type; +} + +int +_pbcP_type(struct _field * field, const char ** type) { + if (field == NULL) { + return 0; + } + int ret = 0; + switch (field->type) { + case PTYPE_DOUBLE: + case PTYPE_FLOAT: + ret = PBC_REAL; + break; + case PTYPE_INT64: + case PTYPE_SINT64: + ret = PBC_INT64; + break; + case PTYPE_INT32: + case PTYPE_SINT32: + ret = PBC_INT; + break; + case PTYPE_UINT32: + case PTYPE_UINT64: + ret = PBC_UINT; + break; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + ret = PBC_FIXED32; + break; + case PTYPE_SFIXED64: + case PTYPE_FIXED64: + ret = PBC_FIXED64; + break; + case PTYPE_BOOL: + ret = PBC_BOOL; + break; + case PTYPE_STRING: + ret = PBC_STRING; + break; + case PTYPE_BYTES: + ret = PBC_BYTES; + break; + case PTYPE_ENUM: + ret = PBC_ENUM; + if (type) { + *type = field->type_name.e->key; + } + break; + case PTYPE_MESSAGE: + ret = PBC_MESSAGE; + if (type) { + *type = field->type_name.m->key; + } + break; + default: + return 0; + } + if (field->label == LABEL_REPEATED || + field->label == LABEL_PACKED) { + ret |= PBC_REPEATED; + } + + return ret; +} + +int +pbc_type(struct pbc_env * p, const char * type_name , const char * key , const char ** type) { + struct _message *m = _pbcP_get_message(p, type_name); + if (m==NULL) { + return 0; + } + if (key == NULL) { + return PBC_NOEXIST; + } + struct _field * field = (struct _field *)_pbcM_sp_query(m->name, key); + return _pbcP_type(field, type); +} diff --git a/luaclib/pb/src/proto.h b/luaclib/pb/src/proto.h new file mode 100644 index 0000000..05bb2d1 --- /dev/null +++ b/luaclib/pb/src/proto.h @@ -0,0 +1,65 @@ +#ifndef PROTOBUFC_PROTO_H +#define PROTOBUFC_PROTO_H + +#include "pbc.h" +#include "map.h" +#include "array.h" +#ifndef _MSC_VER +#include +#endif +#include + +struct map_ip; +struct map_si; +struct map_sp; +struct _message; +struct _enum; + +#define LABEL_OPTIONAL 0 +#define LABEL_REQUIRED 1 +#define LABEL_REPEATED 2 +#define LABEL_PACKED 3 + +struct _field { + int id; + const char *name; + int type; + int label; + pbc_var default_v; + union { + const char * n; + struct _message * m; + struct _enum * e; + } type_name; +}; + +struct _message { + const char * key; + struct map_ip * id; // id -> _field + struct map_sp * name; // string -> _field + struct pbc_rmessage * def; // default message + struct pbc_env * env; +}; + +struct _enum { + const char * key; + struct map_ip * id; + struct map_si * name; + pbc_var default_v; +}; + +struct pbc_env { + struct map_sp * files; // string -> void * + struct map_sp * enums; // string -> _enum + struct map_sp * msgs; // string -> _message + const char * lasterror; +}; + +struct _message * _pbcP_init_message(struct pbc_env * p, const char *name); +void _pbcP_push_message(struct pbc_env * p, const char *name, struct _field *f , pbc_array queue); +struct _enum * _pbcP_push_enum(struct pbc_env * p, const char *name, struct map_kv *table, int sz ); +int _pbcP_message_default(struct _message * m, const char * name, pbc_var defv); +struct _message * _pbcP_get_message(struct pbc_env * p, const char *name); +int _pbcP_type(struct _field * field, const char **type); + +#endif diff --git a/luaclib/pb/src/register.c b/luaclib/pb/src/register.c new file mode 100644 index 0000000..b41ff48 --- /dev/null +++ b/luaclib/pb/src/register.c @@ -0,0 +1,342 @@ +#include "pbc.h" +#include "proto.h" +#include "alloc.h" +#include "map.h" +#include "bootstrap.h" +#include "context.h" +#include "stringpool.h" + +#include +#include + +#ifdef _MSC_VER +#define strtoll _strtoi64 +#endif + +static const char * +_concat_name(struct _stringpool *p , const char *prefix , int prefix_sz , const char *name , int name_sz, int *sz) { + if (prefix_sz == 0) { + if (sz) { + *sz = name_sz; + } + return _pbcS_build(p , name, name_sz); + } + char * temp = (char *)alloca(name_sz + prefix_sz + 2); + memcpy(temp,prefix,prefix_sz); + temp[prefix_sz] = '.'; + memcpy(temp+prefix_sz+1,name,name_sz); + temp[name_sz + prefix_sz + 1] = '\0'; + if (sz) { + *sz = name_sz + prefix_sz + 1; + } + const char * ret = _pbcS_build(p , temp, name_sz + prefix_sz + 1); + return ret; +} + +static void +_register_enum(struct pbc_env *p, struct _stringpool *pool, struct pbc_rmessage * enum_type, const char *prefix, int prefix_sz) { + int field_count = pbc_rmessage_size(enum_type, "value"); + struct map_kv *table = (struct map_kv *)malloc(field_count * sizeof(struct map_kv)); + int i; + for (i=0;itype == PTYPE_STRING || f->type == PTYPE_BYTES) { + f->default_v->s.str = ""; + f->default_v->s.len = 0; + } else { + f->default_v->integer.low = 0; + f->default_v->integer.hi = 0; + } + return; + } + + switch (f->type) { + case PTYPE_DOUBLE: + case PTYPE_FLOAT: + f->default_v->real = strtod(value,NULL); + break; + case PTYPE_STRING: + f->default_v->s.str = _pbcS_build(pool, value , sz); + f->default_v->s.len = sz; + break; + case PTYPE_ENUM: + // enum default value will be converted to f->default_v->e in bootstrap.c : set_field_one() + f->default_v->s.str = value; + f->default_v->s.len = sz; + break; + case PTYPE_BOOL: + if (strcmp(value,"true") == 0) { + f->default_v->integer.low = 1; + } else { + f->default_v->integer.low = 0; + } + f->default_v->integer.hi = 0; + break; + case PTYPE_UINT64: + case PTYPE_INT64: + case PTYPE_SFIXED64: + case PTYPE_SINT64: { + long long v = strtoll(value, NULL, 10); + f->default_v->integer.low = (long) v; + f->default_v->integer.hi = (long)(v >> 32); + break; + } + case PTYPE_INT32: + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + case PTYPE_SINT32: { + int low = strtol(value, NULL, 10); + f->default_v->integer.low = low; + if (low < 0) { + f->default_v->integer.hi = -1; + } else { + f->default_v->integer.hi = 0; + } + break; + } + case PTYPE_UINT32: + f->default_v->integer.low = strtoul(value, NULL, 10); + f->default_v->integer.hi = 0; + break; + case PTYPE_BYTES: + case PTYPE_MESSAGE: + // bytes and message types have no default value + f->default_v->m.buffer = 0; + f->default_v->m.len = 0; + break; + default: + f->default_v->integer.low = 0; + f->default_v->integer.hi = 0; + break; + } +} + +static void +_register_field(struct pbc_rmessage * field, struct _field * f, struct _stringpool *pool) { + f->id = pbc_rmessage_integer(field, "number", 0 , 0); + f->type = pbc_rmessage_integer(field, "type", 0 , 0); // enum + f->label = pbc_rmessage_integer(field, "label", 0, 0) - 1; // LABEL_OPTIONAL = 0 + if (pbc_rmessage_size(field , "options") > 0) { + struct pbc_rmessage * options = pbc_rmessage_message(field, "options" , 0); + int packed = pbc_rmessage_integer(options , "packed" , 0 , NULL); + if (packed) { + f->label = LABEL_PACKED; + } + } + f->type_name.n = pbc_rmessage_string(field, "type_name", 0 , NULL) +1; // abandon prefix '.' + int vsz; + const char * default_value = pbc_rmessage_string(field, "default_value", 0 , &vsz); + _set_default(pool , f , f->type, default_value , vsz); +} + +static void +_register_extension(struct pbc_env *p, struct _stringpool *pool , const char * prefix, int prefix_sz, struct pbc_rmessage * msg, pbc_array queue) { + int extension_count = pbc_rmessage_size(msg , "extension"); + if (extension_count <= 0) + return; + int i; + + const char * last = NULL; + + for (i=0;ifiles, filename)) { + return CHECK_FILE_EXIST; + } + int sz = pbc_rmessage_size(file, "dependency"); + int i; + for (i=0;ifiles, dname) == NULL) { + return CHECK_FILE_DEPENDENCY; + } + } + + *fname = filename; + + return CHECK_FILE_OK; +} + +static int +_register_no_dependency(struct pbc_env * p,struct pbc_rmessage ** files , int n ) { + int r = 0; + int i; + for (i=0;ifiles , filename, pool); + _register(p,files[i],pool); + files[i] = NULL; + } + break; + } + } + return r; +} + +int +pbc_register(struct pbc_env * p, struct pbc_slice *slice) { + struct pbc_rmessage * message = pbc_rmessage_new(p, "google.protobuf.FileDescriptorSet", slice); + if (message == NULL) { + p->lasterror = "register open google.protobuf.FileDescriptorSet fail"; + return 1; + } + int n = pbc_rmessage_size(message, "file"); + struct pbc_rmessage ** files = (struct pbc_rmessage **)alloca(n * sizeof(struct pbc_rmessage *)); + int i; + if (n == 0) { + p->lasterror = "register empty"; + goto _error; + } + for (i=0;ilasterror = "register open fail"; + goto _error; + } + } + + int r = n; + do { + int rr = _register_no_dependency(p,files , n); + if (rr == r) { + p->lasterror = "register dependency error"; + goto _error; + } + r = rr; + } while (r>0); + + pbc_rmessage_delete(message); + return 0; +_error: + pbc_rmessage_delete(message); + return 1; +} diff --git a/luaclib/pb/src/rmessage.c b/luaclib/pb/src/rmessage.c new file mode 100644 index 0000000..11c1246 --- /dev/null +++ b/luaclib/pb/src/rmessage.c @@ -0,0 +1,455 @@ +#include "pbc.h" +#include "alloc.h" +#include "map.h" +#include "context.h" +#include "proto.h" +#include "pattern.h" +#include "varint.h" + +#include +#include + +struct pbc_rmessage { + struct _message * msg; + struct map_sp * index; // key -> struct value * + struct heap * heap; +}; + +union _var { + pbc_var var; + pbc_array array; + struct pbc_rmessage message; +} ; + +struct value { + struct _field * type; + union _var v; +}; + +int +pbc_rmessage_next(struct pbc_rmessage *m, const char **key) { + struct value * v = (struct value *)_pbcM_sp_next(m->index, key); + if (*key == NULL) { + return 0; + } + return _pbcP_type(v->type, NULL); +} + +#define SIZE_VAR (offsetof(struct value, v) + sizeof(pbc_var)) +#define SIZE_ARRAY (offsetof(struct value, v) + sizeof(pbc_array)) +#define SIZE_MESSAGE (offsetof(struct value, v) + sizeof(struct pbc_rmessage)) + +static struct value * +read_string(struct heap *h, struct atom *a,struct _field *f, uint8_t *buffer) { + const char * temp = (const char *) (buffer + a->v.s.start); + int len = a->v.s.end - a->v.s.start; + + if (len > 0 && temp[len-1] == '\0') { + struct value * v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->s.str = temp; + v->v.var->s.len = len; + return v; + } else { + struct value * v = (struct value *)_pbcH_alloc(h, SIZE_VAR + len + 1); + memcpy(((char *)v) + SIZE_VAR , temp, len); + *(((char *)v) + SIZE_VAR + len) = '\0'; + v->v.var->s.str = ((char *)v) + SIZE_VAR; + v->v.var->s.len = len; + return v; + } +} + +static void +read_string_var(struct heap *h, pbc_var var,struct atom *a,struct _field *f,uint8_t *buffer) { + const char * temp = (const char *) (buffer + a->v.s.start); + int len = a->v.s.end - a->v.s.start; + if (len == 0) { + var->s.str = ""; + var->s.len = 0; + } + else if (temp[len-1] == '\0') { + var->s.str = temp; + var->s.len = len; + } else { + char * temp2 = (char *)_pbcH_alloc(h, len + 1); + memcpy(temp2, temp, len); + temp2[len]='\0'; + var->s.str = temp2; + var->s.len = -len; + } +} + +static void _pbc_rmessage_new(struct pbc_rmessage * ret , struct _message * type , void *buffer, int size, struct heap *h); + +static struct value * +read_value(struct heap *h, struct _field *f, struct atom * a, uint8_t *buffer) { + struct value * v; + + switch (f->type) { + case PTYPE_DOUBLE: + CHECK_BIT64(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->real = read_double(a); + break; + case PTYPE_FLOAT: + CHECK_BIT32(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->real = (double) read_float(a); + break; + case PTYPE_ENUM: + CHECK_VARINT(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->e.id = a->v.i.low; + v->v.var->e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , a->v.i.low); + break; + case PTYPE_INT64: + case PTYPE_UINT64: + case PTYPE_INT32: + case PTYPE_UINT32: + case PTYPE_BOOL: + CHECK_VARINT(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->integer = a->v.i; + break; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + CHECK_BIT32(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->integer = a->v.i; + break; + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + CHECK_BIT64(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->integer = a->v.i; + break; + case PTYPE_SINT32: + CHECK_VARINT(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->integer = a->v.i; + _pbcV_dezigzag32(&(v->v.var->integer)); + break; + case PTYPE_SINT64: + CHECK_VARINT(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->integer = a->v.i; + _pbcV_dezigzag64(&(v->v.var->integer)); + break; + case PTYPE_STRING: + CHECK_LEND(a,NULL); + v = read_string(h,a,f,buffer); + break; + case PTYPE_BYTES: + CHECK_LEND(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_VAR); + v->v.var->s.str = (const char *)(buffer + a->v.s.start); + v->v.var->s.len = a->v.s.end - a->v.s.start; + break; + case PTYPE_MESSAGE: + CHECK_LEND(a,NULL); + v = (struct value *)_pbcH_alloc(h, SIZE_MESSAGE); + _pbc_rmessage_new(&(v->v.message), f->type_name.m , + buffer + a->v.s.start , + a->v.s.end - a->v.s.start,h); + break; + default: + return NULL; + } + v->type = f; + return v; +} + +static void +push_value_packed(struct _message * type, pbc_array array, struct _field *f, struct atom * aa, uint8_t *buffer) { + int n = _pbcP_unpack_packed((uint8_t *)buffer + aa->v.s.start, aa->v.s.end - aa->v.s.start, + f->type , array); + if (n<=0) { + // todo : error + type->env->lasterror = "Unpack packed field error"; + return; + } + if (f->type == PTYPE_ENUM) { + int i; + for (i=0;iinteger.low; + v->e.id = id; + v->e.name = (const char*)_pbcM_ip_query(f->type_name.e->id , id); + } + } +} + +static void +push_value_array(struct heap *h, pbc_array array, struct _field *f, struct atom * a, uint8_t *buffer) { + pbc_var v; + + switch (f->type) { + case PTYPE_DOUBLE: + v->real = read_double(a); + break; + case PTYPE_FLOAT: + v->real = (double) read_float(a); + break; + case PTYPE_ENUM: + v->e.id = a->v.i.low; + v->e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , a->v.i.low); + break; + case PTYPE_INT64: + case PTYPE_UINT64: + case PTYPE_INT32: + case PTYPE_UINT32: + case PTYPE_FIXED32: + case PTYPE_FIXED64: + case PTYPE_SFIXED32: + case PTYPE_SFIXED64: + case PTYPE_BOOL: + v->integer = a->v.i; + break; + case PTYPE_SINT32: + v->integer = a->v.i; + _pbcV_dezigzag32(&(v->integer)); + break; + case PTYPE_SINT64: + v->integer = a->v.i; + _pbcV_dezigzag64(&(v->integer)); + break; + case PTYPE_STRING: + CHECK_LEND(a, ); + read_string_var(h,v,a,f,buffer); + break; + case PTYPE_BYTES: + CHECK_LEND(a, ); + v->s.str = (const char *)(buffer + a->v.s.start); + v->s.len = a->v.s.end - a->v.s.start; + break; + case PTYPE_MESSAGE: { + CHECK_LEND(a, ); + struct pbc_rmessage message; + _pbc_rmessage_new(&message, f->type_name.m , + buffer + a->v.s.start , + a->v.s.end - a->v.s.start,h); + if (message.msg == NULL) { + return; + } + v->p[0] = message.msg; + v->p[1] = message.index; + break; + } + default: + return; + } + + _pbcA_push(array,v); +} + +static void +_pbc_rmessage_new(struct pbc_rmessage * ret , struct _message * type , void *buffer, int size , struct heap *h) { + if (size == 0) { + ret->msg = type; + ret->index = _pbcM_sp_new(0 , h); + ret->heap = h; + return; + } + pbc_ctx _ctx; + int count = _pbcC_open(_ctx,buffer,size); + if (count <= 0) { + type->env->lasterror = "rmessage decode context error"; + memset(ret , 0, sizeof(*ret)); + return; + } + struct context * ctx = (struct context *)_ctx; + + ret->msg = type; + ret->index = _pbcM_sp_new(count, h); + ret->heap = h; + + int i; + + for (i=0;inumber;i++) { + int id = ctx->a[i].wire_id >> 3; + struct _field * f = (struct _field *)_pbcM_ip_query(type->id , id); + if (f) { + if (f->label == LABEL_REPEATED || f->label == LABEL_PACKED) { + struct value * v; + void ** vv = _pbcM_sp_query_insert(ret->index, f->name); + if (*vv == NULL) { + v = (struct value *)_pbcH_alloc(h, SIZE_ARRAY); + v->type = f; + _pbcA_open_heap(v->v.array,ret->heap); + *vv = v; + } else { + v= (struct value *)*vv; + } + if (f->label == LABEL_PACKED) { + push_value_packed(type, v->v.array , f , &(ctx->a[i]), (uint8_t *)buffer); + if (pbc_array_size(v->v.array) == 0) { + type->env->lasterror = "rmessage decode packed data error"; + *vv = NULL; + } + } else { + push_value_array(h,v->v.array , f, &(ctx->a[i]), (uint8_t *)buffer); + if (pbc_array_size(v->v.array) == 0) { + type->env->lasterror = "rmessage decode repeated data error"; + *vv = NULL; + } + } + } else { + struct value * v = read_value(h, f, &(ctx->a[i]), (uint8_t *)buffer); + if (v) { + _pbcM_sp_insert(ret->index, f->name, v); + } else { + type->env->lasterror = "rmessage decode data error"; + } + } + } + } + + _pbcC_close(_ctx); +} + +struct pbc_rmessage * +pbc_rmessage_new(struct pbc_env * env, const char * type_name , struct pbc_slice * slice) { + struct _message * msg = _pbcP_get_message(env, type_name); + if (msg == NULL) { + env->lasterror = "Proto not found"; + return NULL; + } + struct pbc_rmessage temp; + struct heap * h = _pbcH_new(slice->len); + _pbc_rmessage_new(&temp, msg , slice->buffer, slice->len , h); + if (temp.msg == NULL) { + _pbcH_delete(h); + return NULL; + } + + struct pbc_rmessage *m = (struct pbc_rmessage *)_pbcH_alloc(temp.heap, sizeof(*m)); + *m = temp; + return m; +} + +void +pbc_rmessage_delete(struct pbc_rmessage * m) { + if (m) { + _pbcH_delete(m->heap); + } +} + +const char * +pbc_rmessage_string(struct pbc_rmessage * m , const char *key , int index, int *sz) { + struct value * v = (struct value *)_pbcM_sp_query(m->index,key); + int type = 0; + pbc_var var; + if (v == NULL) { + type = _pbcP_message_default(m->msg, key, var); + } else { + if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) { + _pbcA_index(v->v.array, index, var); + } else { + var[0] = v->v.var[0]; + } + type = v->type->type; + } + + if (type == PTYPE_ENUM) { + if (sz) { + *sz = strlen(var->e.name); + } + return var->e.name; + } + + if (sz) { + int len = var->s.len; + if (len<0) { + len = - len; + } + *sz = len; + } + return var->s.str; +} + +uint32_t +pbc_rmessage_integer(struct pbc_rmessage *m , const char *key , int index, uint32_t *hi) { + struct value * v = (struct value *)_pbcM_sp_query(m->index,key); + pbc_var var; + int type = 0; + if (v == NULL) { + type = _pbcP_message_default(m->msg, key, var); + } else { + if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) { + _pbcA_index(v->v.array, index, var); + } else { + var[0] = v->v.var[0]; + } + type = v->type->type; + } + + if (type == PTYPE_ENUM) { + if (hi) { + *hi = 0; + } + return var->e.id; + } + + if (hi) { + *hi = var->integer.hi; + } + return var->integer.low; +} + +double +pbc_rmessage_real(struct pbc_rmessage * m, const char *key , int index) { + struct value * v = (struct value *)_pbcM_sp_query(m->index,key); + pbc_var var; + if (v == NULL) { + _pbcP_message_default(m->msg, key, var); + } else { + if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) { + _pbcA_index(v->v.array, index, var); + } else { + return v->v.var->real; + } + } + return var->real; +} + + +struct pbc_rmessage * +pbc_rmessage_message(struct pbc_rmessage * rm, const char *key, int index) { + struct value * v = (struct value *)_pbcM_sp_query(rm->index,key); + if (v == NULL) { + struct _field * f = (struct _field *)_pbcM_sp_query(rm->msg->name, key); + if (f == NULL) { + rm->msg->env->lasterror = "Invalid key for sub-message"; + // invalid key + return NULL; + } + struct _message * m = f->type_name.m; + + if (m->def == NULL) { + // m->def will be free at the end (pbc_delete). + m->def = (struct pbc_rmessage *)malloc(sizeof(struct pbc_rmessage)); + m->def->msg = m; + m->def->index = NULL; + } + return m->def; + } else { + if (v->type->label == LABEL_REPEATED) { + return (struct pbc_rmessage *)_pbcA_index_p(v->v.array,index); + } else { + return &(v->v.message); + } + } +} + +int +pbc_rmessage_size(struct pbc_rmessage *m, const char *key) { + struct value * v = (struct value *)_pbcM_sp_query(m->index,key); + if (v == NULL) { + return 0; + } + if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) { + return pbc_array_size(v->v.array); + } else { + return 1; + } +} \ No newline at end of file diff --git a/luaclib/pb/src/stringpool.c b/luaclib/pb/src/stringpool.c new file mode 100644 index 0000000..9e64458 --- /dev/null +++ b/luaclib/pb/src/stringpool.c @@ -0,0 +1,60 @@ +#include "alloc.h" + +#include +#include + +#define PAGE_SIZE 256 + +struct _stringpool { + char * buffer; + size_t len; + struct _stringpool *next; +}; + +struct _stringpool * +_pbcS_new(void) { + struct _stringpool * ret = (struct _stringpool *)malloc(sizeof(struct _stringpool) + PAGE_SIZE); + ret->buffer = (char *)(ret + 1); + ret->len = 0; + ret->next = NULL; + return ret; +} + +void +_pbcS_delete(struct _stringpool *pool) { + while(pool) { + struct _stringpool *next = pool->next; + free(pool); + pool = next; + } +} + +const char * +_pbcS_build(struct _stringpool *pool, const char * str , int sz) { + size_t s = sz + 1; + if (s < PAGE_SIZE - pool->len) { + char * ret = pool->buffer + pool->len; + memcpy(pool->buffer + pool->len, str, s); + pool->len += s; + return ret; + } + if (s > PAGE_SIZE) { + struct _stringpool * next = (struct _stringpool *)malloc(sizeof(struct _stringpool) + s); + next->buffer = (char *)(next + 1); + memcpy(next->buffer, str, s); + next->len = s; + next->next = pool->next; + pool->next = next; + return next->buffer; + } + struct _stringpool *next = (struct _stringpool *)malloc(sizeof(struct _stringpool) + PAGE_SIZE); + next->buffer = pool->buffer; + next->next = pool->next; + next->len = pool->len; + + pool->next = next; + pool->buffer = (char *)(next + 1); + memcpy(pool->buffer, str, s); + pool->len = s; + return pool->buffer; +} diff --git a/luaclib/pb/src/stringpool.h b/luaclib/pb/src/stringpool.h new file mode 100644 index 0000000..394423d --- /dev/null +++ b/luaclib/pb/src/stringpool.h @@ -0,0 +1,10 @@ +#ifndef PROTOBUF_C_STRINGPOOL_H +#define PROTOBUF_C_STRINGPOOL_H + +struct _stringpool; + +struct _stringpool * _pbcS_new(void); +void _pbcS_delete(struct _stringpool *pool); +const char * _pbcS_build(struct _stringpool *pool, const char * str , int sz); + +#endif diff --git a/luaclib/pb/src/varint.c b/luaclib/pb/src/varint.c new file mode 100644 index 0000000..928581d --- /dev/null +++ b/luaclib/pb/src/varint.c @@ -0,0 +1,110 @@ +#include "varint.h" + +#include "pbc.h" + +#include + +inline int +_pbcV_encode32(uint32_t number, uint8_t buffer[10]) +{ + if (number < 0x80) { + buffer[0] = (uint8_t) number ; + return 1; + } + buffer[0] = (uint8_t) (number | 0x80 ); + if (number < 0x4000) { + buffer[1] = (uint8_t) (number >> 7 ); + return 2; + } + buffer[1] = (uint8_t) ((number >> 7) | 0x80 ); + if (number < 0x200000) { + buffer[2] = (uint8_t) (number >> 14); + return 3; + } + buffer[2] = (uint8_t) ((number >> 14) | 0x80 ); + if (number < 0x10000000) { + buffer[3] = (uint8_t) (number >> 21); + return 4; + } + buffer[3] = (uint8_t) ((number >> 21) | 0x80 ); + buffer[4] = (uint8_t) (number >> 28); + return 5; +} + +int +_pbcV_encode(uint64_t number, uint8_t buffer[10]) +{ + if ((number & 0xffffffff) == number) { + return _pbcV_encode32((uint32_t)number , buffer); + } + int i = 0; + do { + buffer[i] = (uint8_t)(number | 0x80); + number >>= 7; + ++i; + } while (number >= 0x80); + buffer[i] = (uint8_t)number; + return i+1; +} + +int +_pbcV_decode(uint8_t buffer[10], struct longlong *result) { + if (!(buffer[0] & 0x80)) { + result->low = buffer[0]; + result->hi = 0; + return 1; + } + uint32_t r = buffer[0] & 0x7f; + int i; + for (i=1;i<4;i++) { + r |= ((buffer[i]&0x7f) << (7*i)); + if (!(buffer[i] & 0x80)) { + result->low = r; + result->hi = 0; + return i+1; + } + } + uint64_t lr = 0; + for (i=4;i<10;i++) { + lr |= ((uint64_t)(buffer[i] & 0x7f) << (7*(i-4))); + if (!(buffer[i] & 0x80)) { + result->hi = (uint32_t)(lr >> 4); + result->low = r | (((uint32_t)lr & 0xf) << 28); + return i+1; + } + } + + result->low = 0; + result->hi = 0; + return 10; +} + +int +_pbcV_zigzag32(int32_t n, uint8_t buffer[10]) +{ + n = (n << 1) ^ (n >> 31); + return _pbcV_encode32(n,buffer); +} + +int +_pbcV_zigzag(int64_t n, uint8_t buffer[10]) +{ + n = (n << 1) ^ (n >> 63); + return _pbcV_encode(n,buffer); +} + +void +_pbcV_dezigzag64(struct longlong *r) +{ + uint32_t low = r->low; + r->low = ((low >> 1) | ((r->hi & 1) << 31)) ^ - (low & 1); + r->hi = (r->hi >> 1) ^ - (low & 1); +} + +void +_pbcV_dezigzag32(struct longlong *r) +{ + uint32_t low = r->low; + r->low = (low >> 1) ^ - (low & 1); + r->hi = -(low >> 31); +} diff --git a/luaclib/pb/src/varint.h b/luaclib/pb/src/varint.h new file mode 100644 index 0000000..000882e --- /dev/null +++ b/luaclib/pb/src/varint.h @@ -0,0 +1,20 @@ +#ifndef PROTOBUF_C_VARINT_H +#define PROTOBUF_C_VARINT_H + +#include + +struct longlong { + uint32_t low; + uint32_t hi; +}; + +int _pbcV_encode32(uint32_t number, uint8_t buffer[10]); +int _pbcV_encode(uint64_t number, uint8_t buffer[10]); +int _pbcV_zigzag32(int32_t number, uint8_t buffer[10]); +int _pbcV_zigzag(int64_t number, uint8_t buffer[10]); + +int _pbcV_decode(uint8_t buffer[10], struct longlong *result); +void _pbcV_dezigzag64(struct longlong *r); +void _pbcV_dezigzag32(struct longlong *r); + +#endif diff --git a/luaclib/pb/src/wmessage.c b/luaclib/pb/src/wmessage.c new file mode 100644 index 0000000..975672b --- /dev/null +++ b/luaclib/pb/src/wmessage.c @@ -0,0 +1,518 @@ +#include "pbc.h" +#include "context.h" +#include "alloc.h" +#include "varint.h" +#include "map.h" +#include "proto.h" + +#include +#include +#include + +#ifndef _MSC_VER +#include +#endif + +#define WMESSAGE_SIZE 64 + +struct pbc_wmessage { + struct _message *type; + uint8_t * buffer; + uint8_t * ptr; + uint8_t * endptr; + pbc_array sub; + struct map_sp *packed; + struct heap * heap; +}; + +struct _packed { + int id; + int ptype; + pbc_array data; +}; + +static struct pbc_wmessage * +_wmessage_new(struct heap *h, struct _message *msg) { + struct pbc_wmessage * m = (struct pbc_wmessage *)_pbcH_alloc(h, sizeof(*m)); + m->type = msg; + m->buffer = (uint8_t *)_pbcH_alloc(h, WMESSAGE_SIZE); + m->ptr = m->buffer; + m->endptr = m->buffer + WMESSAGE_SIZE; + _pbcA_open_heap(m->sub, h); + m->packed = NULL; + m->heap = h; + + return m; +} + +struct pbc_wmessage * +pbc_wmessage_new(struct pbc_env * env, const char *type_name) { + struct _message * msg = _pbcP_get_message(env, type_name); + if (msg == NULL) + return NULL; + struct heap *h = _pbcH_new(0); + return _wmessage_new(h, msg); +} + +void +pbc_wmessage_delete(struct pbc_wmessage *m) { + if (m) { + _pbcH_delete(m->heap); + } +} + +static void +_expand_message(struct pbc_wmessage *m, int sz) { + if (m->ptr + sz > m->endptr) { + int cap = m->endptr - m->buffer; + sz = m->ptr + sz - m->buffer; + do { + cap = cap * 2; + } while ( sz > cap ) ; + int old_size = m->ptr - m->buffer; + uint8_t * buffer = (uint8_t *)_pbcH_alloc(m->heap, cap); + memcpy(buffer, m->buffer, old_size); + m->ptr = buffer + (m->ptr - m->buffer); + m->endptr = buffer + cap; + m->buffer = buffer; + } +} + +static struct _packed * +_get_packed(struct pbc_wmessage *m , struct _field *f , const char *key) { + if (m->packed == NULL) { + m->packed = _pbcM_sp_new(4, m->heap); + } + void ** v = _pbcM_sp_query_insert(m->packed , key); + if (*v == NULL) { + *v = _pbcH_alloc(m->heap, sizeof(struct _packed)); + struct _packed *p = (struct _packed *)*v; + p->id = f->id; + p->ptype = f->type; + _pbcA_open_heap(p->data, m->heap); + return p; + } + return (struct _packed *)*v; +} + +static void +_packed_integer(struct pbc_wmessage *m, struct _field *f, const char *key , uint32_t low, uint32_t hi) { + struct _packed * packed = _get_packed(m,f,key); + pbc_var var; + var->integer.low = low; + var->integer.hi = hi; + _pbcA_push(packed->data , var); +} + +static void +_packed_real(struct pbc_wmessage *m, struct _field *f, const char *key , double v) { + struct _packed * packed = _get_packed(m,f,key); + pbc_var var; + var->real = v; + _pbcA_push(packed->data , var); +} + +static inline void +int64_encode(uint32_t low, uint32_t hi , uint8_t * buffer) { + buffer[0] = (uint8_t)(low & 0xff); + buffer[1] = (uint8_t)(low >> 8 & 0xff); + buffer[2] = (uint8_t)(low >> 16 & 0xff); + buffer[3] = (uint8_t)(low >> 24 & 0xff); + buffer[4] = (uint8_t)(hi & 0xff); + buffer[5] = (uint8_t)(hi >> 8 & 0xff); + buffer[6] = (uint8_t)(hi >> 16 & 0xff); + buffer[7] = (uint8_t)(hi >> 24 & 0xff); +} + +static inline void +int32_encode(uint32_t low, uint8_t * buffer) { + buffer[0] = (uint8_t)(low & 0xff); + buffer[1] = (uint8_t)(low >> 8 & 0xff); + buffer[2] = (uint8_t)(low >> 16 & 0xff); + buffer[3] = (uint8_t)(low >> 24 & 0xff); +} + +int +pbc_wmessage_integer(struct pbc_wmessage *m, const char *key, uint32_t low, uint32_t hi) { + struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key); + if (f==NULL) { + // todo : error + m->type->env->lasterror = "wmessage_interger query key error"; + return -1; + } + if (f->label == LABEL_PACKED) { + _packed_integer(m , f, key , low, hi); + return 0; + } + if (f->label == LABEL_OPTIONAL) { + if (f->type == PTYPE_ENUM) { + if (low == f->default_v->e.id) + return 0; + } else { + // if (low == f->default_v->integer.low && + // hi == f->default_v->integer.hi) { + // return 0; + // } + } + } + int id = f->id << 3; + + _expand_message(m,20); + switch (f->type) { + case PTYPE_INT64: + case PTYPE_UINT64: + case PTYPE_INT32: + id |= WT_VARINT; + m->ptr += _pbcV_encode32(id, m->ptr); + m->ptr += _pbcV_encode((uint64_t)low | (uint64_t)hi << 32 , m->ptr); + break; + case PTYPE_UINT32: + case PTYPE_ENUM: + case PTYPE_BOOL: + id |= WT_VARINT; + m->ptr += _pbcV_encode32(id, m->ptr); + m->ptr += _pbcV_encode32(low, m->ptr); + break; + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + id |= WT_BIT64; + m->ptr += _pbcV_encode32(id, m->ptr); + int64_encode(low,hi,m->ptr); + m->ptr += 8; + break; + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + id |= WT_BIT32; + m->ptr += _pbcV_encode32(id, m->ptr); + int32_encode(low,m->ptr); + m->ptr += 4; + break; + case PTYPE_SINT32: + id |= WT_VARINT; + m->ptr += _pbcV_encode32(id, m->ptr); + m->ptr += _pbcV_zigzag32(low, m->ptr); + break; + case PTYPE_SINT64: + id |= WT_VARINT; + m->ptr += _pbcV_encode32(id, m->ptr); + m->ptr += _pbcV_zigzag((uint64_t)low | (uint64_t)hi << 32 , m->ptr); + break; + } + + return 0; +} + +int +pbc_wmessage_real(struct pbc_wmessage *m, const char *key, double v) { + struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key); + if (f == NULL) { + // todo : error + m->type->env->lasterror = "wmessage_real query key error"; + return -1; + } + if (f->label == LABEL_PACKED) { + _packed_real(m , f, key , v); + return 0; + } + + if (f->label == LABEL_OPTIONAL) { + // if (v == f->default_v->real) + // return 0; + } + int id = f->id << 3; + _expand_message(m,18); + switch (f->type) { + case PTYPE_FLOAT: { + id |= WT_BIT32; + m->ptr += _pbcV_encode32(id, m->ptr); + float_encode(v , m->ptr); + m->ptr += 4; + break; + } + case PTYPE_DOUBLE: + id |= WT_BIT64; + m->ptr += _pbcV_encode32(id, m->ptr); + double_encode(v , m->ptr); + m->ptr += 8; + break; + } + + return 0; +} + +int +pbc_wmessage_string(struct pbc_wmessage *m, const char *key, const char * v, int len) { + struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key); + if (f == NULL) { + // todo : error + m->type->env->lasterror = "wmessage_string query key error"; + return -1; + } + + bool varlen = false; + + if (len <=0) { + varlen = true; + // -1 for add '\0' + len = strlen(v) - len; + } + if (f->label == LABEL_PACKED) { + if (f->type == PTYPE_ENUM) { + char * temp = (char *)alloca(len + 1); + if (!varlen || v[len] != '\0') { + memcpy(temp,v,len); + temp[len]='\0'; + v = temp; + } + int enum_id = 0; + int err = _pbcM_si_query(f->type_name.e->name, v , &enum_id); + if (err) { + // todo : error , invalid enum + m->type->env->lasterror = "wmessage_string packed invalid enum"; + return -1; + } + _packed_integer(m , f, key , enum_id , 0); + } + return 0; + } + + if (f->label == LABEL_OPTIONAL) { + if (f->type == PTYPE_ENUM) { + if (strncmp(v , f->default_v->e.name, len) == 0 && f->default_v->e.name[len] =='\0') { + return 0; + } + } else if (f->type == PTYPE_STRING) { + // if (len == f->default_v->s.len && + // strcmp(v, f->default_v->s.str) == 0) { + // return 0; + // } + } else if (f->type == PTYPE_BYTES) { + // if (len == 0) { + // return 0; + // } + } + } + int id = f->id << 3; + _expand_message(m,20); + switch (f->type) { + case PTYPE_ENUM : { + char * temp = (char *)alloca(len+1); + if (!varlen || v[len] != '\0') { + memcpy(temp,v,len); + temp[len]='\0'; + v = temp; + } + int enum_id = 0; + int err = _pbcM_si_query(f->type_name.e->name, v, &enum_id); + if (err) { + // todo : error , enum invalid + m->type->env->lasterror = "wmessage_string invalid enum"; + return -1; + } + id |= WT_VARINT; + m->ptr += _pbcV_encode32(id, m->ptr); + m->ptr += _pbcV_encode32(enum_id, m->ptr); + break; + } + case PTYPE_STRING: + case PTYPE_BYTES: + id |= WT_LEND; + m->ptr += _pbcV_encode32(id, m->ptr); + m->ptr += _pbcV_encode32(len, m->ptr); + _expand_message(m,len); + memcpy(m->ptr , v , len); + m->ptr += len; + break; + } + + return 0; +} + +struct pbc_wmessage * +pbc_wmessage_message(struct pbc_wmessage *m, const char *key) { + struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key); + if (f == NULL) { + // todo : error + m->type->env->lasterror = "wmessage_message query key error"; + return NULL; + } + pbc_var var; + var->p[0] = _wmessage_new(m->heap, f->type_name.m); + var->p[1] = f; + _pbcA_push(m->sub , var); + return (struct pbc_wmessage *)var->p[0]; +} + +static void +_pack_packed_64(struct _packed *p,struct pbc_wmessage *m) { + int n = pbc_array_size(p->data); + int len = n * 8; + int i; + pbc_var var; + _expand_message(m,10 + len); + m->ptr += _pbcV_encode32(len, m->ptr); + switch (p->ptype) { + case PTYPE_DOUBLE: + for (i=0;idata, i, var); + double_encode(var->real , m->ptr + i * 8); + } + break; + default: + for (i=0;idata, i, var); + int64_encode(var->integer.low , var->integer.hi, m->ptr + i * 8); + } + break; + } + m->ptr += len; +} + +static void +_pack_packed_32(struct _packed *p,struct pbc_wmessage *m) { + int n = pbc_array_size(p->data); + int len = n * 4; + int i; + pbc_var var; + _expand_message(m,10 + len); + m->ptr += _pbcV_encode32(len, m->ptr); + switch (p->ptype) { + case PTYPE_FLOAT: + for (i=0;idata, i, var); + float_encode(var->real , m->ptr + i * 8); + } + break; + default: + for (i=0;idata, i, var); + int32_encode(var->integer.low , m->ptr + i * 8); + } + break; + } + m->ptr += len; +} + +static void +_pack_packed_varint(struct _packed *p,struct pbc_wmessage *m) { + int n = pbc_array_size(p->data); + + int offset = m->ptr - m->buffer; + int len = n * 2; + if (p->ptype == PTYPE_BOOL) { + len = n; + } + int i; + pbc_var var; + _expand_message(m,10 + len); + int len_len = _pbcV_encode32(len, m->ptr); + m->ptr += len_len; + + switch (p->ptype) { + case PTYPE_INT64: + case PTYPE_UINT64: + for (i=0;idata, i, var); + _expand_message(m,10); + m->ptr += _pbcV_encode((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32 , m->ptr); + } + break; + case PTYPE_INT32: + case PTYPE_BOOL: + case PTYPE_UINT32: + case PTYPE_ENUM: + for (i=0;idata, i, var); + _expand_message(m,10); + m->ptr += _pbcV_encode32(var->integer.low , m->ptr); + } + break; + case PTYPE_SINT32: + for (i=0;idata, i, var); + _expand_message(m,10); + m->ptr += _pbcV_zigzag32(var->integer.low, m->ptr); + } + break; + case PTYPE_SINT64: + for (i=0;idata, i, var); + _expand_message(m,10); + m->ptr += _pbcV_zigzag((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32 , m->ptr); + } + break; + default: + // error + memset(m->ptr , 0 , n); + m->ptr += n; + m->type->env->lasterror = "wmessage type error when pack packed"; + break; + } + int end_offset = m->ptr - m->buffer; + int end_len = end_offset - (offset + len_len); + if (end_len != len) { + uint8_t temp[10]; + int end_len_len = _pbcV_encode32(end_len, temp); + if (end_len_len != len_len) { + _expand_message(m, end_len_len); + memmove(m->buffer + offset + end_len_len , + m->buffer + offset + len_len , + end_len); + m->ptr += end_len_len - len_len; + } + memcpy(m->buffer + offset , temp, end_len_len); + } +} + +static void +_pack_packed(void *p, void *ud) { + struct _packed *packed = (struct _packed *)p; + struct pbc_wmessage * m = (struct pbc_wmessage *)ud; + int id = packed->id << 3 | WT_LEND; + _expand_message(m,10); + m->ptr += _pbcV_encode32(id, m->ptr); + switch(packed->ptype) { + case PTYPE_DOUBLE: + case PTYPE_FIXED64: + case PTYPE_SFIXED64: + _pack_packed_64(packed,m); + break; + case PTYPE_FLOAT: + case PTYPE_FIXED32: + case PTYPE_SFIXED32: + _pack_packed_32(packed,m); + break; + default: + _pack_packed_varint(packed,m); + break; + } +} + +void * +pbc_wmessage_buffer(struct pbc_wmessage *m, struct pbc_slice *slice) { + if (m->packed) { + _pbcM_sp_foreach_ud(m->packed , _pack_packed, m); + } + int i; + int n = pbc_array_size(m->sub); + for (i=0;isub, i , var); + struct pbc_slice s; + pbc_wmessage_buffer((struct pbc_wmessage *)var->p[0] , &s); + if (s.buffer) { + struct _field * f = (struct _field *)var->p[1]; + int id = f->id << 3 | WT_LEND; + _expand_message(m,20+s.len); + m->ptr += _pbcV_encode32(id, m->ptr); + m->ptr += _pbcV_encode32(s.len, m->ptr); + memcpy(m->ptr, s.buffer, s.len); + m->ptr += s.len; + } + } + slice->buffer = m->buffer; + slice->len = m->ptr - m->buffer; + + return m->buffer; +} + diff --git a/luaclib/pb/test/addressbook.c b/luaclib/pb/test/addressbook.c new file mode 100644 index 0000000..5b69fc7 --- /dev/null +++ b/luaclib/pb/test/addressbook.c @@ -0,0 +1,132 @@ +#include "pbc.h" + +#include +#include +#include +#include + +static void +read_file (const char *filename , struct pbc_slice *slice) { + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + slice->buffer = NULL; + slice->len = 0; + return; + } + fseek(f,0,SEEK_END); + slice->len = ftell(f); + fseek(f,0,SEEK_SET); + slice->buffer = malloc(slice->len); + fread(slice->buffer, 1 , slice->len , f); + fclose(f); +} + + +static void +dump(uint8_t *buffer, int sz) { + int i , j; + for (i=0;i=32 && c<127) { + printf("%c",c); + } else { + printf("."); + } + } + printf("\n"); + } + } + + printf("\n"); +} + +static void +test_rmessage(struct pbc_env *env, struct pbc_slice *slice) { + struct pbc_rmessage * m = pbc_rmessage_new(env, "tutorial.Person", slice); + if (m==NULL) { + printf("Error : %s",pbc_error(env)); + return; + } + printf("name = %s\n", pbc_rmessage_string(m , "name" , 0 , NULL)); + printf("id = %d\n", pbc_rmessage_integer(m , "id" , 0 , NULL)); + printf("email = %s\n", pbc_rmessage_string(m , "email" , 0 , NULL)); + + int phone_n = pbc_rmessage_size(m, "phone"); + int i; + const char * field_name; + pbc_type(env, "tutorial.Person", "phone", &field_name); + printf("phone type [%s]\n",field_name); + + for (i=0;i + +int +main() +{ + pbc_array array; + pbc_var v; + + _pbcA_open(array); + + int i ; + + for (i=0;i<100;i++) { + v->real = (double)i; + printf("push %d\n",i); + _pbcA_push(array, v); + } + + int s = pbc_array_size(array); + + for (i=0;ireal); + } + + _pbcA_close(array); + + return 0; +} diff --git a/luaclib/pb/test/decode.c b/luaclib/pb/test/decode.c new file mode 100644 index 0000000..1640e9b --- /dev/null +++ b/luaclib/pb/test/decode.c @@ -0,0 +1,99 @@ +#include "pbc.h" + +#include +#include +#include + +static void +read_file (const char *filename , struct pbc_slice *slice) { + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + slice->buffer = NULL; + slice->len = 0; + return; + } + fseek(f,0,SEEK_END); + slice->len = ftell(f); + fseek(f,0,SEEK_SET); + slice->buffer = malloc(slice->len); + fread(slice->buffer, 1 , slice->len , f); + fclose(f); +} + +static void +decode_all(void *ud , int type, const char * typename , union pbc_value *v, int id, const char *key) { + printf("%s : ", key ) ; + switch(type & ~PBC_REPEATED) { + case PBC_MESSAGE: + printf("[%s] -> \n" , typename); + pbc_decode(ud, typename, &(v->s), decode_all, ud); + printf("---------\n"); + break; + case PBC_INT: + printf("%d\n", (int)v->i.low); + break; + case PBC_REAL: + printf("%lf\n", v->f); + break; + case PBC_BOOL: + printf("<%s>\n", v->i.low ? "true" : "false"); + break; + case PBC_ENUM: + printf("[%s:%d]\n", v->e.name , v->e.id); + break; + case PBC_STRING: { + char buffer[v->s.len+1]; + memcpy(buffer, v->s.buffer, v->s.len); + buffer[v->s.len] = '\0'; + printf("\"%s\"\n", buffer); + break; + } + case PBC_BYTES: { + int i; + uint8_t *buffer = v->s.buffer; + for (i=0;is.len;i++) { + printf("%02X ",buffer[i]); + } + printf("\n"); + break; + } + case PBC_INT64: { + printf("0x%x%08x\n",v->i.hi, v->i.low); + break; + } + case PBC_UINT: + printf("%u\n",v->i.low); + break; + default: + printf("!!! %d\n", type); + break; + } +} + +void +test_decode(struct pbc_env * env , const char * pb) +{ + struct pbc_slice slice; + read_file(pb, &slice); + + pbc_decode(env, "google.protobuf.FileDescriptorSet", &slice, decode_all , env); + + int ret = pbc_register(env, &slice); + + printf("Register %d\n",ret); + + free(slice.buffer); +} + +int +main(int argc, char *argv[]) +{ + struct pbc_env * env = pbc_new(); + + test_decode(env,argv[1]); + + pbc_delete(env); + + + return 0; +} diff --git a/luaclib/pb/test/descriptor.proto b/luaclib/pb/test/descriptor.proto new file mode 100644 index 0000000..c951b63 --- /dev/null +++ b/luaclib/pb/test/descriptor.proto @@ -0,0 +1,539 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "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 COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + + + +package google.protobuf; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field whithout harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + } + repeated ExtensionRange extension_range = 5; + + optional MessageOptions options = 7; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; // Not ZigZag encoded. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. + TYPE_UINT64 = 4; + TYPE_INT32 = 5; // Not ZigZag encoded. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; // Tag-delimited aggregate. + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + }; + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + // TODO(sanjay): Should we add LABEL_MAP? + }; + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be either TYPE_ENUM or TYPE_MESSAGE. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + optional string default_value = 7; + + optional FieldOptions options = 8; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; +} + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. +// +// Clients may define custom options as extensions of the *Options messages. +// These extensions may not yet be known at parsing time, so the parser cannot +// store the values in them. Instead it stores them in a field in the *Options +// message called uninterpreted_option. This field must have the same name +// across all *Options messages. We then use this field to populate the +// extensions when we build a descriptor, at which point all protos have been +// parsed and so all extensions are known. +// +// Extension numbers for custom options may be chosen as follows: +// * For options which will only be used within a single application or +// organization, or for experimental options, use field numbers 50000 +// through 99999. It is up to you to ensure that you do not use the +// same number for multiple options. +// * For options which will be published and used publicly by multiple +// independent entities, e-mail protobuf-global-extension-registry@google.com +// to reserve extension numbers. Simply provide your project name (e.g. +// Object-C plugin) and your porject website (if available) -- there's no need +// to explain how you intend to use them. Usually you only need one extension +// number. You can declare multiple options with only one extension number by +// putting them in a sub-message. See the Custom Options section of the docs +// for examples: +// http://code.google.com/apis/protocolbuffers/docs/proto.html#options +// If this turns out to be popular, a web service will be set up +// to automatically assign option numbers. + + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + optional string java_outer_classname = 8; + + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default=false]; + + // If set true, then the Java code generator will generate equals() and + // hashCode() methods for all messages defined in the .proto file. This is + // purely a speed optimization, as the AbstractMessage base class includes + // reflection-based implementations of these methods. + optional bool java_generate_equals_and_hash = 20 [default=false]; + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + } + optional OptimizeMode optimize_for = 9 [default=SPEED]; + + + + + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of proto2. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default=false]; + optional bool java_generic_services = 17 [default=false]; + optional bool py_generic_services = 18 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default=false]; + + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + optional bool no_standard_descriptor_accessor = 2 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1 [default = STRING]; + enum CType { + // Default mode. + STRING = 0; + + CORD = 1; + + STRING_PIECE = 2; + } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. + optional bool packed = 2; + + + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + optional bool deprecated = 3 [default=false]; + + // EXPERIMENTAL. DO NOT USE. + // For "map" fields, the name of the field in the enclosed type that + // is the key for this map. For example, suppose we have: + // message Item { + // required string name = 1; + // required string value = 2; + // } + // message Config { + // repeated Item items = 1 [experimental_map_key="name"]; + // } + // In this situation, the map key for Item will be set to "name". + // TODO: Fully-implement this, then remove the "experimental_" prefix. + optional string experimental_map_key = 9; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumOptions { + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumValueOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +message UninterpretedOption { + // The name of the uninterpreted option. Each string represents a segment in + // a dot-separated name. is_extension is true iff a segment represents an + // extension (denoted with parentheses in options specs in .proto files). + // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents + // "foo.(bar.baz).qux". + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } + repeated NamePart name = 2; + + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendent. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition. For + // example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed=true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed=true]; + + // TODO(kenton): Record comments appearing before and after the + // declaration. + } +} diff --git a/luaclib/pb/test/float.c b/luaclib/pb/test/float.c new file mode 100644 index 0000000..797a2b6 --- /dev/null +++ b/luaclib/pb/test/float.c @@ -0,0 +1,89 @@ +#include "pbc.h" + +#include +#include +#include +#include + +static void +read_file (const char *filename , struct pbc_slice *slice) { + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + slice->buffer = NULL; + slice->len = 0; + return; + } + fseek(f,0,SEEK_END); + slice->len = ftell(f); + fseek(f,0,SEEK_SET); + slice->buffer = malloc(slice->len); + fread(slice->buffer, 1 , slice->len , f); + fclose(f); +} + + +static void +dump(uint8_t *buffer, int sz) { + int i , j; + for (i=0;i=32 && c<127) { + printf("%c",c); + } else { + printf("."); + } + } + printf("\n"); + } + } + + printf("\n"); +} + +static void +test_rmessage(struct pbc_env *env, struct pbc_slice *slice) { + struct pbc_rmessage * m = pbc_rmessage_new(env, "real", slice); + printf("f = %f\n", pbc_rmessage_real(m , "f" , 0 )); + printf("d = %f\n", pbc_rmessage_real(m , "d" , 0 )); + pbc_rmessage_delete(m); +} + +static struct pbc_wmessage * +test_wmessage(struct pbc_env * env) +{ + struct pbc_wmessage * msg = pbc_wmessage_new(env, "real"); + + pbc_wmessage_real(msg, "f", 1.0); + pbc_wmessage_real(msg, "d" , 4.0); + + return msg; +} + +int +main() +{ + struct pbc_slice slice; + read_file("float.pb", &slice); + if (slice.buffer == NULL) + return 1; + struct pbc_env * env = pbc_new(); + pbc_register(env, &slice); + + free(slice.buffer); + + struct pbc_wmessage *msg = test_wmessage(env); + + pbc_wmessage_buffer(msg, &slice); + + dump(slice.buffer, slice.len); + + test_rmessage(env, &slice); + + pbc_wmessage_delete(msg); + pbc_delete(env); + + return 0; +} diff --git a/luaclib/pb/test/float.proto b/luaclib/pb/test/float.proto new file mode 100644 index 0000000..e1f10c2 --- /dev/null +++ b/luaclib/pb/test/float.proto @@ -0,0 +1,4 @@ +message real { + optional float f = 1; + optional double d = 2; +} diff --git a/luaclib/pb/test/map.c b/luaclib/pb/test/map.c new file mode 100644 index 0000000..80776c1 --- /dev/null +++ b/luaclib/pb/test/map.c @@ -0,0 +1,51 @@ +#include "src/map.h" + +#include +#include + +int +main() +{ + struct map_kv kv[] = { + {1,"alice"}, + {3,"bob" }, + {99,"carol"}, + }; + + struct map_ip * map = _pbcM_ip_new(kv, sizeof(kv)/sizeof(kv[0])); + struct map_si * map2 = _pbcM_si_new(kv, sizeof(kv)/sizeof(kv[0])); + int i; + + for (i=0;i<100;i++) { + void *p= _pbcM_ip_query(map,i); + if (p) { + int id = 0; + _pbcM_si_query(map2,p,&id); + printf("%d %s\n",id,(const char *)p); + } + } + + struct map_sp * map3 = _pbcM_sp_new(0, NULL); + _pbcM_sp_insert(map3,"Alice","alice"); + _pbcM_sp_insert(map3,"Bob","bob"); + + void ** r = _pbcM_sp_query_insert(map3, "Carol"); + *r = "carol"; + + r = _pbcM_sp_query_insert(map3, "Alice"); + *r = "not alice"; + + printf("%s\n",(const char *)_pbcM_sp_query(map3,"Alice")); + printf("%s\n",(const char *)_pbcM_sp_query(map3,"Bob")); + printf("%s\n",(const char *)_pbcM_sp_query(map3,"Carol")); + + const char * key = NULL; + for (;;) { + void * v = _pbcM_sp_next(map3, &key); + if (key == NULL) + break; + printf("%s : %s\n", key, (const char *)v); + } + + return 0; +} diff --git a/luaclib/pb/test/pattern.c b/luaclib/pb/test/pattern.c new file mode 100644 index 0000000..0f35351 --- /dev/null +++ b/luaclib/pb/test/pattern.c @@ -0,0 +1,192 @@ +#include "pbc.h" + +#include +#include +#include +#include + +static void +read_file (const char *filename , struct pbc_slice *slice) { + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + slice->buffer = NULL; + slice->len = 0; + return; + } + fseek(f,0,SEEK_END); + slice->len = ftell(f); + fseek(f,0,SEEK_SET); + slice->buffer = malloc(slice->len); + fread(slice->buffer, 1 , slice->len , f); + fclose(f); +} + +static void +dump(uint8_t *buffer, int sz) { + int i , j; + for (i=0;i=32 && c<127) { + printf("%c",c); + } else { + printf("."); + } + } + printf("\n"); + } + } + + printf("\n"); +} + +static struct pbc_pattern *pat; +static struct pbc_pattern *pat_phone; + +struct person_phone { + struct pbc_slice number; + int32_t type; +}; + +struct person { + struct pbc_slice name; + int32_t id; + struct pbc_slice email; + pbc_array phone; + pbc_array test; +}; + + +static void +test_pattern_unpack(struct pbc_env *env, struct pbc_slice * slice) { + struct person p; + int r = pbc_pattern_unpack(pat, slice, &p); + if (r>=0) { + printf("name = %s\n",(const char *)p.name.buffer); + printf("id = %d\n",p.id); + printf("email = %s\n",(const char *)p.email.buffer); + int n = pbc_array_size(p.phone); + int i; + for (i=0;ilen = 0; + return slice->len; + } + + pbc_array_push_slice(p.phone, &phone_slice); + + pbc_pattern_set_default(pat_phone, &phone); + + phone.number.buffer = (void *)"87654321"; + phone.number.len = -1; + + char temp2[128]; + struct pbc_slice phone_slice2 = { temp2, sizeof(temp2) }; + + unused = pbc_pattern_pack(pat_phone, &phone , &phone_slice2); + + if (unused < 0) { + slice->len = 0; + return slice->len; + } + + pbc_array_push_slice(p.phone, &phone_slice2); + + int i; + for (i=0;i<3;i++) { + pbc_array_push_integer(p.test, -i*4,0); + } + + int r = pbc_pattern_pack(pat, &p, slice); + + pbc_pattern_close_arrays(pat,&p); + printf("pack into %d bytes\n", slice->len); + + return r; +} + +int +main() +{ + struct pbc_slice slice; + read_file("addressbook.pb", &slice); + if (slice.buffer == NULL) + return 1; + struct pbc_env * env = pbc_new(); + pbc_register(env, &slice); + + free(slice.buffer); + + pat = pbc_pattern_new(env, "tutorial.Person" , + "name %s id %d email %s phone %a test %a", + offsetof(struct person, name) , + offsetof(struct person, id) , + offsetof(struct person, email) , + offsetof(struct person, phone) , + offsetof(struct person, test)); + + pat_phone = pbc_pattern_new(env, "tutorial.Person.PhoneNumber", + "number %s type %d", + offsetof(struct person_phone, number), + offsetof(struct person_phone, type)); + + + char buffer[4096]; + struct pbc_slice message = { buffer, sizeof(buffer) }; + + test_pattern_pack(env, &message); + + dump(message.buffer, message.len); + + test_pattern_unpack(env, &message); + + pbc_pattern_delete(pat); + pbc_pattern_delete(pat_phone); + + pbc_delete(env); + + return 0; +} diff --git a/luaclib/pb/test/pbc.c b/luaclib/pb/test/pbc.c new file mode 100644 index 0000000..e9a7732 --- /dev/null +++ b/luaclib/pb/test/pbc.c @@ -0,0 +1,69 @@ +#include "pbc.h" + +#include +#include + +static void +read_file (const char *filename , struct pbc_slice *slice) { + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + slice->buffer = NULL; + slice->len = 0; + return; + } + fseek(f,0,SEEK_END); + slice->len = ftell(f); + fseek(f,0,SEEK_SET); + slice->buffer = malloc(slice->len); + fread(slice->buffer, 1 , slice->len , f); + fclose(f); +} + +void +test_des(struct pbc_env * env , const char * pb) +{ + struct pbc_slice slice; + read_file(pb, &slice); + + struct pbc_rmessage * msg = pbc_rmessage_new(env, "google.protobuf.FileDescriptorSet", &slice); + + struct pbc_rmessage * file = pbc_rmessage_message(msg,"file",0); + + printf("name = %s\n",pbc_rmessage_string(file, "name", 0 , NULL)); + printf("package = %s\n",pbc_rmessage_string(file, "package", 0 , NULL)); + + int sz = pbc_rmessage_size(file, "dependency"); + printf("dependency[%d] =\n" , sz); + int i; + for (i=0;i +#include +#include + +#define COUNT 1000000 + +static void +read_file (const char *filename , struct pbc_slice *slice) { + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + slice->buffer = NULL; + slice->len = 0; + return; + } + fseek(f,0,SEEK_END); + slice->len = ftell(f); + fseek(f,0,SEEK_SET); + slice->buffer = malloc(slice->len); + fread(slice->buffer, 1 , slice->len , f); + fclose(f); +} + +static void +test(struct pbc_env *env) { + int i; + for(i=0; i + +#include "varint.h" +#include "pbc.h" + +static void +dump(uint8_t buffer[10], int s) +{ + int i; + for (i=0;i +#include +#include +#include + +#include "pbc.h" + +static void +read_file(const char *filename , struct pbc_slice *slice) { + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "Can't open file %s\n", filename); + exit(1); + } + fseek(f,0,SEEK_END); + slice->len = ftell(f); + fseek(f,0,SEEK_SET); + slice->buffer = malloc(slice->len); + fread(slice->buffer, 1 , slice->len , f); + fclose(f); +} + +static void +dump_bytes(const char *data, size_t len) { + size_t i; + for (i = 0; i < len; i++) + if (i == 0) + fprintf(stdout, "%02x", 0xff & data[i]); + else + fprintf(stdout, " %02x", 0xff & data[i]); +} + +static void dump_message(struct pbc_rmessage *m, int level); + +static void +dump_value(struct pbc_rmessage *m, const char *key, int type, int idx, int level) { + int i; + for (i=0;i= data->len) { + data->len *= 2; + data->buffer = realloc(data->buffer, data->len); + } + ((uint8_t *)data->buffer)[idx] = (uint8_t)byte; +} + +static void +read_stdin(int mode, struct pbc_slice *data) { + data->len = 128; + data->buffer = malloc(data->len); + int idx = 0; + while(!feof(stdin)) { + int byte; + int r = scanf("%d" , &byte); + if (r == 0) { + break; + } + push_byte(byte, data, idx); + ++idx; + } + data->len = idx; +} + +static void +usage(const char *argv0) { + printf(" -h help.\n" + " -p protobuf file\n" + " -m \n" + " -d \n" + " -D input from stdin (DEC number)\n" + ); +} + +int +main(int argc , char * argv[]) +{ + int ch; + const char * proto = NULL; + const char * message = NULL; + const char * datafile = NULL; + int mode = 0; + while ((ch = getopt(argc, argv, "hDp:m:d:")) != -1) { + switch(ch) { + case 'h': + usage(argv[0]); + return 0; + case 'p': + proto = optarg; + break; + case 'm': + message = optarg; + break; + case 'd': + datafile = optarg; + break; + case 'D': + mode = 10; + break; + default: + usage(argv[0]); + return 1; + } + } + + if (proto == NULL || message == NULL) { + usage(argv[0]); + return 1; + } + + struct pbc_slice data; + + if (datafile == NULL) { + read_stdin(mode, &data); + } else { + read_file(datafile , &data); + } + + dump(proto , message , &data); + + return 0; +} + diff --git a/lualib/http.lua b/lualib/http.lua index ccd2c3f..c6e7a0a 100644 --- a/lualib/http.lua +++ b/lualib/http.lua @@ -39,6 +39,10 @@ local function receiveheaders(sock, headers) if err then return nil, err end -- headers go until a blank line is found while line ~= "" do + --header ends if line == "\r\n" + if line == "\r\n" then + return headers + end -- get field-name and value name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) if not (name and value) then return nil, "malformed reponse headers" end @@ -46,10 +50,10 @@ local function receiveheaders(sock, headers) -- get next line (value might be folded) line, err = sock:receive() if err then return nil, err end - -- unfold any folded values - while string.find(line, "^%s") do + -- unfold any folded values(header parts begin with [ \t]!) + while string.find(line, "^[ \t]") do value = value .. line - line = sock:receive() + line, err = sock:receive() if err then return nil, err end end -- save pair in table diff --git a/lualib/ltn12.lua b/lualib/ltn12.lua index b42689a..ffdd7c4 100644 --- a/lualib/ltn12.lua +++ b/lualib/ltn12.lua @@ -38,7 +38,8 @@ end -- chains a bunch of filters together -- (thanks to Wim Couwenberg) function filter.chain(...) - local n = table.getn(arg) + local arg = { ... } + local n = #arg local top, index = 1, 1 local retry = "" return function(chunk) @@ -186,6 +187,7 @@ end -- other, as if they were concatenated -- (thanks to Wim Couwenberg) function source.cat(...) + local arg = {...} local src = table.remove(arg, 1) return function() while src do diff --git a/lualib/socket.lua b/lualib/socket.lua index 7ec753e..382bd03 100644 --- a/lualib/socket.lua +++ b/lualib/socket.lua @@ -72,7 +72,7 @@ end socket.sinkt["default"] = socket.sinkt["keep-open"] -socket.sink = socket.choose(sinkt) +socket.sink = socket.choose(socket.sinkt) socket.sourcet["by-length"] = function(sock, length) return base.setmetatable({ @@ -111,7 +111,6 @@ end socket.sourcet["default"] = socket.sourcet["until-closed"] - -socket.source = socket.choose(sourcet) +socket.source = socket.choose(socket.sourcet) return socket \ No newline at end of file diff --git a/lualib/socket_core.lua b/lualib/socket_core.lua index 7dcaf5a..69feff2 100644 --- a/lualib/socket_core.lua +++ b/lualib/socket_core.lua @@ -103,6 +103,7 @@ end function lua_tcp_mode:settimeout(timeout) self._timeout = timeout + return true end function lua_tcp_mode:setoption(field, value) @@ -136,53 +137,62 @@ local function try_read(tcp_socket) tcp_socket._received_buffer:append(buffer) end else - tcp_socket._sock:close() - tcp_socket._sock = nil - tcp_socket._received_buffer = nil + tcp_socket:close() return context.strerror(buffer) end end -function lua_tcp_mode:receive(len) +function lua_tcp_mode:receive(len, header) local sock = self._sock if not sock or sock:is_closed() then - return false, "socket not invalid or closed" + return "", "socket not invalid or closed" end if not len or len == '*l' then if not self._received_buffer or self._received_buffer:length() == 0 then local error = try_read(self) if error then - return false, error + return "", error end end while true do local len = self._received_buffer:find("\n") if len then - return self._received_buffer:split(len):tostring() + local received = self._received_buffer:split(len):tostring() + return header and header..received or received end local error = try_read(self) if error then - return false, error + return "", error end end end if type(len) == "number" then if len <= 0 then - return false, "receive length can't be less than 0" + return "", "receive length can't be less than 0" end while not self._received_buffer or self._received_buffer:length() < len do local error = try_read(self) if error then - return false, error + return "", error end end - return self._received_buffer:split(len):tostring() + local received = self._received_buffer:split(len):tostring() + return header and header..received or received end - return false, "unsupported receive format" + return "", "unsupported receive format" +end + +function lua_tcp_mode:close() + if self._sock then + self._sock:close() + self._sock = nil + self._received_buffer = nil + end + return true end socket.tcp = lua_tcp_mode diff --git a/lualib/url.lua b/lualib/url.lua index ebf99fa..950ff21 100644 --- a/lualib/url.lua +++ b/lualib/url.lua @@ -254,7 +254,7 @@ function parse_path(path) path = path or "" --path = string.gsub(path, "%s", "") string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) - for i = 1, table.getn(parsed) do + for i = 1, #parsed do parsed[i] = unescape(parsed[i]) end if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end @@ -272,7 +272,7 @@ end ----------------------------------------------------------------------------- function build_path(parsed, unsafe) local path = "" - local n = table.getn(parsed) + local n = #parsed if unsafe then for i = 1, n-1 do path = path .. parsed[i] diff --git a/node-lua.cbp b/node-lua.cbp deleted file mode 100644 index 87a9c2c..0000000 --- a/node-lua.cbp +++ /dev/null @@ -1,336 +0,0 @@ - - - - - - diff --git a/node-lua.cscope_file_list b/node-lua.cscope_file_list deleted file mode 100644 index ed38cad..0000000 --- a/node-lua.cscope_file_list +++ /dev/null @@ -1,105 +0,0 @@ -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\uv-bsd.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lstring.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\openbsd.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\uv-darwin.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lstring.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\pipe.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\uv-linux.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lstrlib.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\poll.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\uv-sunos.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\ltable.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\process.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\uv-unix.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\ltable.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\proctitle.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\uv-win.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\ltablib.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\signal.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\ltm.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\stream.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lfunc.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\aix.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\ldo.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\ltm.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\sunos.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lgc.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\async.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\ldebug.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lua.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lgc.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\core.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lauxlib.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lua.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\linit.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\cygwin.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\ldump.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lua.hpp" -"E:\Kuaipan\workspace\node-lua\deps\lua\liolib.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\darwin-proctitle.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lbaselib.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\luac.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\llex.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\darwin.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lauxlib.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\luaconf.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\llex.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\dl.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\ldblib.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lualib.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\llimits.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\error.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\ldebug.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lundump.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lmathlib.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\freebsd.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lcode.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lundump.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lmem.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\fs.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lcode.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lvm.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lmem.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\fsevents.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lctype.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lvm.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\loadlib.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\getaddrinfo.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lfunc.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lzio.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\tcp.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lobject.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\internal.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\ldo.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\thread.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lobject.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\kqueue.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lbitlib.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\threadpool.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lopcodes.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\linux-core.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lapi.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\timer.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lopcodes.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\linux-inotify.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lapi.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\tty.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\loslib.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\linux-syscalls.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lctype.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\udp.c" -"E:\Kuaipan\workspace\node-lua\deps\lua\lzio.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lparser.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\linux-syscalls.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lcorolib.c" -"E:\Kuaipan\workspace\node-lua\src\main.cpp" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\ngx-queue.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lparser.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\loop-watcher.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\stdint-msvc2008.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lstate.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\loop.c" -"E:\Kuaipan\workspace\node-lua\deps\uv\include\uv-private\tree.h" -"E:\Kuaipan\workspace\node-lua\deps\lua\lstate.h" -"E:\Kuaipan\workspace\node-lua\deps\uv\src\unix\netbsd.c" diff --git a/node-lua.layout b/node-lua.layout deleted file mode 100644 index 4f70e6e..0000000 --- a/node-lua.layout +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/node-lua.lua b/node-lua.lua index f5891ef..34c6a03 100644 --- a/node-lua.lua +++ b/node-lua.lua @@ -1,55 +1,6 @@ -1. result, listen_socket = tcp.listen(addr, port[, backlog, [void (*listen_callback)(result, listen_socket, addr, port[, backlog])]]) - --listen_callback is a once callback, blocking if callback is nil. -2. result, listen_socket = tcp.listens(sock_name[, void (*listen_callback)(result, listen_socket, sock_name)]) - --listen_callback is a once callback, blocking if callback is nil. -3. result, listen_socket = tcp.listen6(addr, port[, backlog, [void (*listen_callback)(result, listen_socket, addr, port[, backlog])]]) - --listen_callback is a once callback, blocking if callback is nil. -4. result, accept_socket = tcp.accept(listen_socket[, timeout]) - --accept_callback is a continues callback, blocking if callback is nil. -5. result, accept_socket = tcp.accept(listen_socket[, void (*accept_callback)(result, accept_socket, listen_socket)]) - --accept_callback is a continues callback, blocking if callback is nil. -6. result, connect_socket = tcp.connect(host_addr, host_port[, timeout [, void (*connect_callback)(result, connect_socket, host_addr, host_port[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. -7. result, connect_socket = tcp.connects(sock_name[, timeout [, void (*connect_callback)(result, connect_socket, host_addr, host_port[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. -8. result, connect_socket = tcp.connect6(host_addr, host_port[, timeout [, void (*connect_callback)(result, connect_socket, host_addr, host_port[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. -9. result, buffer = tcp.read(socket[, timeout]) - --recv_callback is a continues callback, blocking if callback is nil. -10. result, buffer = tcp.read(socket[, void (*recv_callback)(result, buffer, socket)]) - --recv_callback is a continues callback, blocking if callback is nil. -11. result, error = tcp.write(socket, buffer_or_lstring[, void (*send_callback)(result, error, socket, buffer_or_lstring)/bool safety]) - --send_callback is a once callback and is safety assurance, blocking until buffer_or_lstring is sent only if safety is true. -12. tcp.set_rwopt(socket, option_table) - --set tcp_socket read and write options --e.g. { "read_head_endian" = "L", "read_head_bytes" = 2, "read_head_max" = 65535, } -13. tcp.get_rwopt(socket) - --get tcp_socket read and write options - -14. tcp.set_nodelay(socket, enable) - -15. result, error = context.send(handle, data) --QUERY - -16. result, query_data = context.query(handle, data[, timeout [, query_callback(result, query_data, handle, data[, timeout])]]) --QUERY(real query) - --query_callback is a once callback, blocking if callback is nil. -17. result, error = context.reply(handle, session, data) --REPLY - -18. result, data, recv_handle, session = context.recv(handle[, timeout]) - --recv_callback is a continues callback, blocking if callback is nil. -19. result, data, recv_handle, session = context.recv(handle[, recv_callback(result, data, recv_handle, session, handle)]) - --recv_callback is a continues callback, blocking if callback is nil. -20. result, error = context.wait(handle[, timeout[, callback(result, error, handle[, timeout])]]) - --connect_callback is a once callback, blocking if callback is nil. -21. timer.sleep(seconds) - -22. timer.timeout(seconds, ..., void (*callback)(...)) - -23. timer.loop(interval, repeat_time, ..., void (*callback)(...)) - ----------------------------------------------------------------------------------------------------------------------------- - -14. 考虑lua_service的gc[考虑一直没有唤醒的服务的lua垃圾回收] - -17. 研究lua hook,如何中断死循环和调试(输入) +14. 考虑lua_service的定时gc[考虑一直没有唤醒的服务的lua垃圾回收] + +17. 研究lua hook,如何中断死循环和远程调试(输入) 22.to be implemented / to be fixed @@ -57,10 +8,21 @@ 24.lua socket tcp socket 迁移 +25.如何支持数据共享,解决内存过大问题:提供共享api + +26.热更新 + io 阻塞非阻塞? setvbuf {"__tostring", f_tostring}, 热更新 +优化lua shared proto 锁,不使用原子锁? +udp tty 在线调试 + +freebsd: +1. pkg 命令安装软件 pkg安装pkg自身 pkg install +2. sshd 服务器配置 /etc/ssh/sshd_config : PermitRootLogin yes +3. make 和 其它操作系统不一样,需要安装gmake warning 编译lua的共享dll库,需要在编译时开启 LUA_BUILD_AS_DLL 这个宏(以便在vc下边下编译时导出符号) (外部模块库需要同时开启 LUA_CORE 这个宏,以便使用 LUAMOD_API 这个宏来导出luaopen接口) warning 使用lua的dll扩展库,需要把lua整个打成dll动态库,主进程和其它扩展库都需要使用动态链接lua库的方式来编译或生成,不然lua的版本校验将不会通过(动态库实际使用lua.dll,编译和链接时使用lua.lib) @@ -72,4 +34,41 @@ For examples, see my page of Lua libraries: http://www.tecgraf.puc-rio.br/~lhf/f You can link liblua.a statically into your main program but you have to export its symbols by adding -Wl,-E at link time. That's how the Lua interpreter is built in Linux. -A node completation of lua which support sync and async remote procedure call, and task-multiplexing support(in muti-thread mode with no useless wakeup) \ No newline at end of file +A node completation of lua which support sync and async remote procedure call, and task-multiplexing support(in muti-thread mode with no useless wakeup) + + +未初始化的指针错误: +有一个选择打开和关闭SDL检查的位置就是:项目属性->配置属性->C/C++->SDL检查,选测是或者否。 + +按照编译错误的提示来看应该是编译器没有识别inline参数。 +查阅了一下inline是c++里面的东西,在c里面使用是会发生错误。 +#define inline __inline + +Makefile 添加宏定义: -DDEBUG +传递参数: make PLATFORM=aaaa + +VS中添加预处理宏的方法: +除了在.c及.h中添加宏定义之外,还可以采用如下方法添加宏定义: +1、若只需要定义一个宏(如#define DEBUG),可以右键点击工程-->属性-->c/c++-->预处理器-->预处理器定义,点击下拉框中的编辑,输入想要定义的宏; +2、如果还需要定义宏的内容(如#define inline __inline),可以右键点击工程-->属性-->c/c++-->命令行,在其它选项中输入如下内容: /D"inline"=__inline 。 + + +snprintf()函数并不是标准c/c++中规定的函数,所以在许多编译器中,厂商提供了其相应的实现的版本。 +在gcc中,该函数名称就 snprintf(),而在VS中称为 _snprintf。 +所以在需要使用 snprintf()时改成_snprintf就可以了,或则在预编译处加入: +#if _MSC_VER +#define snprintf _snprintf +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + +#define __STDC_LIMIT_MACROS + + + 将VS2010工程提交给Git管理时需要哪些文件: + *.h *.cpp *.sln *.vcxproj *.vcxproj.filters *.qrc + 以及Resources目录下的资源文件。 + 如果使用Git的过滤配置,则还需要.gitignore文件。 + 其他的诸如*.suo *.sdf *.opensdf *.vcxproj.user均可以过滤掉! + + diff --git a/node-lua.sln b/node-lua.sln index a3f509f..eaa274a 100644 --- a/node-lua.sln +++ b/node-lua.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "node-lua", "node-lua.vcxproj", "{3FC7B490-78F8-4A14-812B-02D6AFA48F96}" ProjectSection(ProjectDependencies) = postProject @@ -13,11 +13,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uv", "deps\uv\uv.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lua", "deps\lua\lua.vcxproj", "{1F43B07D-0625-4E5D-A419-53BB749B2293}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mime", "clib\mime\mime.vcxproj", "{2691C520-B01B-46AB-B585-716767F72A9F}" - ProjectSection(ProjectDependencies) = postProject - {1F43B07D-0625-4E5D-A419-53BB749B2293} = {1F43B07D-0625-4E5D-A419-53BB749B2293} - EndProjectSection -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -36,10 +31,6 @@ Global {1F43B07D-0625-4E5D-A419-53BB749B2293}.Debug|Win32.Build.0 = Debug|Win32 {1F43B07D-0625-4E5D-A419-53BB749B2293}.Release|Win32.ActiveCfg = Release|Win32 {1F43B07D-0625-4E5D-A419-53BB749B2293}.Release|Win32.Build.0 = Release|Win32 - {2691C520-B01B-46AB-B585-716767F72A9F}.Debug|Win32.ActiveCfg = Debug|Win32 - {2691C520-B01B-46AB-B585-716767F72A9F}.Debug|Win32.Build.0 = Debug|Win32 - {2691C520-B01B-46AB-B585-716767F72A9F}.Release|Win32.ActiveCfg = Release|Win32 - {2691C520-B01B-46AB-B585-716767F72A9F}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/node-lua.suo b/node-lua.suo deleted file mode 100644 index 4aeb038..0000000 Binary files a/node-lua.suo and /dev/null differ diff --git a/node-lua.v12.suo b/node-lua.v12.suo deleted file mode 100644 index 82dc3a7..0000000 Binary files a/node-lua.v12.suo and /dev/null differ diff --git a/node-lua.vcproj b/node-lua.vcproj deleted file mode 100644 index fe6a72e..0000000 --- a/node-lua.vcproj +++ /dev/null @@ -1,715 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/node-lua.vcproj.SOCODING.Administrator.user b/node-lua.vcproj.SOCODING.Administrator.user deleted file mode 100644 index d10f43a..0000000 --- a/node-lua.vcproj.SOCODING.Administrator.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/node-lua.vcxproj b/node-lua.vcxproj index 29bbfc4..39c3410 100644 --- a/node-lua.vcxproj +++ b/node-lua.vcxproj @@ -46,7 +46,7 @@ true - $(SolutionDir)$(Configuration)\ + $(ProjectDir) $(Configuration)\ false @@ -54,7 +54,7 @@ Disabled deps\lua;deps\uv\include;deps\uv\include\uv-private;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;LUA_COMPAT_ALL;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;LUA_COMPAT_5_2;LUA_COMPAT_5_1;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -75,7 +75,7 @@ MaxSpeed true deps\lua;deps\uv\include;deps\uv\include\uv-private;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + _CONSOLE;LUA_COMPAT_5_2;LUA_COMPAT_5_1;%(PreprocessorDefinitions) MultiThreadedDLL true @@ -83,12 +83,13 @@ ProgramDatabase - ws2_32.lib;psapi.lib;iphlpapi.lib;%(AdditionalDependencies) + uv.lib;nlua.lib;ws2_32.lib;psapi.lib;iphlpapi.lib;%(AdditionalDependencies) true Console true true MachineX86 + deps\uv;deps\lua;%(AdditionalLibraryDirectories) @@ -96,22 +97,29 @@ + + + + + + + @@ -119,12 +127,14 @@ + + @@ -132,19 +142,25 @@ + + + + + + diff --git a/node-lua.vcxproj.filters b/node-lua.vcxproj.filters index 0a3ae1e..f8f1a9d 100644 --- a/node-lua.vcxproj.filters +++ b/node-lua.vcxproj.filters @@ -96,6 +96,30 @@ include + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + @@ -167,5 +191,26 @@ src + + src + + + src + + + src + + + src + + + src + + + src + + + src + \ No newline at end of file diff --git a/node-lua.vcxproj.user b/node-lua.vcxproj.user deleted file mode 100644 index 19ec781..0000000 --- a/node-lua.vcxproj.user +++ /dev/null @@ -1,7 +0,0 @@ - - - - main.lua - WindowsLocalDebugger - - \ No newline at end of file diff --git a/sample/agent.lua b/sample/agent.lua new file mode 100644 index 0000000..aeb4db7 --- /dev/null +++ b/sample/agent.lua @@ -0,0 +1,14 @@ + +local fd, write_count = ... +fd = tonumber(fd) +write_count = tonumber(write_count) + +local counter = 0 +context.recv(0, function(result, recv_handle, session, type, data, data2) + counter = counter + 1 + --print("agent["..(context.self).."] recved: ", result, recv_handle, session, type, data, data2) + tcp.write(fd, data) + if counter >= write_count then + context.recv(0, nil) + end +end) diff --git a/sample/client.lua b/sample/client.lua new file mode 100644 index 0000000..55aac93 --- /dev/null +++ b/sample/client.lua @@ -0,0 +1,18 @@ + +local host, port, write_count = ... +write_count = tonumber(write_count) +local result, socket = tcp.connect(host, tonumber(port)) +if result then + socket:set_rwopt({ read_head_endian = "L", read_head_bytes = 2, read_head_max = 65535, write_head_endian = "L", write_head_bytes = 2, write_head_max = 65535, }) + for i = 1, write_count do + socket:write("client wrote: "..(context.self).."_"..i) + end + for i = 1, write_count do + local result, data = socket:read() + if result then + --print("client readed "..(context.self).."_"..i .." :", data) + end + end +else + print("tcp.connect connect failed: ", context.strerror(socket)) +end \ No newline at end of file diff --git a/sample/gate.lua b/sample/gate.lua new file mode 100644 index 0000000..a5087cc --- /dev/null +++ b/sample/gate.lua @@ -0,0 +1,51 @@ + +local client_count = 100 +local write_count = 100 +local fd_clents = {} +local agent_clents = {} + +local function socket_read_func(result, buffer, socket) + local fd = socket:fd() + local client = fd_clents[fd] + if not result then + if client then + fd_clents[fd] = nil + agent_clents[client.agent] = nil + context.destroy(client.agent, "socket closed: " .. context.strerror(buffer)) + end + socket:close() + return + end + --print("socket_read_func(result, buffer, socket)", result, buffer, socket) + context.send(client.agent, 1, buffer) +end + +local result, listen_socket = tcp.listen("127.0.0.1", 9901) +if result then + listen_socket:accept(function(result, accept_socket) + if result then + local fd = accept_socket:fd() + accept_socket:set_rwopt({ read_head_endian = "L", read_head_bytes = 2, read_head_max = 65535, write_head_endian = "L", write_head_bytes = 2, write_head_max = 65535, }) + accept_socket:set_wshared(true) + local agent = context.create("sample/agent.lua", fd, write_count) + local client = { fd = fd, agent = agent, addr = accept_socket:remote_addr(), } + fd_clents[fd] = client + agent_clents[agent] = client + accept_socket:read(socket_read_func) + else + print("server accept error: ", context.strerror(accept_socket)) + end + end) + local clients = {} + for i = 1, client_count do --n clients will create n agent + clients[i] = context.create("sample/client.lua", "127.0.0.1", 9901, write_count) + end + for i = 1, #clients do + context.wait(clients[i]) + --print("client "..i .. "waited back!") + end + listen_socket:close() +else + print("server listen error: ", context.strerror(listen_socket)) +end + diff --git a/sample/main.lua b/sample/main.lua index dba28e2..597082d 100644 --- a/sample/main.lua +++ b/sample/main.lua @@ -70,6 +70,63 @@ print("value", value1, value2)]] -- # define TEST_PIPENAME_2 "/tmp/uv-test-sock2" -- #endif +--assert(false, 1, 2, 3) +local data = bson.encode({ a = 1, b = 2, }) +local t = bson.decode(data) +print(type(data), t, t.a, t.b) + +local buffer = buffer.new("hello world!") +context.log(1, buffer, type(buffer)) +assert(false, buffer) + +do return end + +local result, server = udp.open("127.0.0.1", 8081) +server:read(function(result, buffer, remote_addr, remote_port, remote_ipv6, server) + print("server read callback: ", result, buffer, remote_addr, remote_port, remote_ipv6, server) + server:write("hello, world server send!", remote_addr, remote_port, true) +end) + +local result, client1 = udp.open() +local result, client2 = udp.open() + +client1:read(function(result, buffer, remote_addr, remote_port, remote_ipv6, client) + print("client1 read callback: ", result, buffer, remote_addr, remote_port, remote_ipv6, client) + client1:close() +end) +client2:read(function(result, buffer, remote_addr, remote_port, remote_ipv6, client) + print("client2 read callback: ", result, buffer, remote_addr, remote_port, remote_ipv6, client) + client2:close() +end) + +print("client1:write ", client1:write("hello, world1!", "127.0.0.1", 8081, true)) +print("client2:write ", client2:write("hello, world2!", "127.0.0.1", 8081, true)) + +print("client1:addr ", client1:local_addr(), client1:local_port(), client1:remote_addr(), client1:remote_port()) +print("client2:addr ", client2:local_addr(), client2:local_port(), client2:remote_addr(), client2:remote_port()) + +timer.sleep(0.1) +server:close() + +--timer.sleep(3600) + +do return end + +--context send and recv test, memery leak test + +local handle = context.create("sample/test.lua") + +print("main service create sub service:", handle) + +context.send(handle, 1, 2, 3, 4, nil, true, false, "hello", { 1, 2, 3, 4, nil, true, false, ["main"] = 1, }) +context.send(handle, 1, 2, 3, 4, nil, true, false, "hello", { 1, 2, 3, 4, nil, true, false, ["main"] = 1, }) +print("context.query", context.timed_query(handle, 0.5, "hello world")) +context.destroy(handle) + +do return end + +-- print(context.self, context.parent, handle) + if context.winos then package.cpath = package.cpath ..";".."..\\clib\\?.dll" package.path = package.path ..";".."..\\lualib\\?.lua" @@ -78,27 +135,67 @@ else package.path = package.path ..";".."../lualib/?.lua" end -for i = 1, 32 do - context.create("test.lua", 1, 2, 3) -end -do return end +-- for i = 1, 32 do + -- context.create("test.lua", 1, 2, 3) +-- end +-- do return end + +acceptSockets = {} +connecSockets = {} local ret, sock = tcp.listen("0.0.0.0", 8801) if not ret then print("tcp.listens failed: ", context.strerror(sock)) else print("tcp.listens success: ", sock) - sock:accept(function(result, accept_sock) print("sock:accept", result, accept_sock) sock:close() accept_sock:set_nodelay(false) accept_sock:write("hello world!") end) - local ret, con_sock = tcp.connect("127.0.0.1", 8801) - if ret then - print("tcp.connect", ret, con_sock) - local ret, buffer = con_sock:read() - print("tcp read", ret, ret and buffer:tostring()) - else - print("tcp.connect", ret, context.strerror(sock)) + sock:accept(function(result, accept_sock) + if result then + acceptSockets[#acceptSockets+1] = accept_sock + print("sock:accept success", #acceptSockets, accept_sock:fd(), accept_sock:local_port()) + else + print("sock:accept fail", ret, context.strerror(accept_sock)) + end + --sock:close() + --accept_sock:set_nodelay(false) + -- print("accept_sock local addr:", accept_sock:local_addr(), accept_sock:local_port()) + -- print("accept_sock remote addr:", accept_sock:remote_addr(), accept_sock:remote_port()) + -- accept_sock:set_rwopt({ read_head_endian = "L", read_head_bytes = 2, read_head_max = 65535, write_head_endian = "L", write_head_bytes = 2, write_head_max = 65535,}) + -- accept_sock:set_wshared(true) + -- print("accept_sock:fd", accept_sock:fd()) + -- tcp.write(accept_sock:fd(), "hello world!", true) + -- accept_sock:write("hello world!", function(result, error) print("tcp.write1", result, error) end) + -- tcp.write(accept_sock:fd(), "hello world!", function(result, error) print("tcp.write2", result, error) end) + -- accept_sock:close() + -- tcp.write(accept_sock:fd(), "hello world!", function(result, error) print("tcp.write3", result, error) end) + end) + for i = 1, 100 do + local ret, con_sock = tcp.connect("127.0.0.1", 8801) + if ret then + connecSockets[#connecSockets+1] = con_sock + print("tcp.connect success", #connecSockets, con_sock:fd(), con_sock:remote_port(), con_sock:local_port()) + -- print("tcp.connect", ret, con_sock) + -- print("con_sock local addr:", con_sock:local_addr(), con_sock:local_port()) + -- print("con_sock remote addr:", con_sock:remote_addr(), con_sock:remote_port()) + -- con_sock:set_rwopt({ read_head_endian = "L", read_head_bytes = 2, read_head_max = 65535, write_head_endian = "L", write_head_bytes = 2, write_head_max = 65535,}) + -- con_sock:set_wshared(true) + -- local ret, buffer = con_sock:read() + -- print("tcp read", ret, ret and buffer:tostring()) + -- local ret, buffer = con_sock:read() + -- print("tcp read", ret, ret and buffer:tostring()) + -- local ret, buffer = con_sock:read() + -- print("tcp read", ret, ret and buffer:tostring()) + -- local ret, buffer = con_sock:read() + -- print("tcp read", ret, ret and buffer:tostring()) + else + print("tcp.connect fail", ret, context.strerror(con_sock), #connecSockets) + break + end end + sock:close() end +--timer.sleep(1) + do return end diff --git a/sample/test.lua b/sample/test.lua index e8981a8..67795b2 100644 --- a/sample/test.lua +++ b/sample/test.lua @@ -1,21 +1,53 @@ --print(package.path) -package.path = package.path .. ";..\\lualib\\?.lua" -package.cpath = package.cpath .. ";..\\clib\\?.dll" - -local redis = require 'redis' -local client = redis.connect("10.4.0.32", 6379, 2) -client:set('usr:nrk', "hello world") -client:set('usr:nobody', 5) -local value1 = client:get('usr:nrk') -local value2 = client:get('usr:nobody') -print("value", value1, value2) - -local http = require 'http' -print(http.request("www.baidu.com")) - -function gethttpsize(u) - local r, c, h = http.request {method = "HEAD", url = u} - return r, c, h +-- print(context.parent) +if context.winos then + package.path = package.path .. ";..\\lualib\\?.lua" + package.cpath = package.cpath .. ";..\\luaclib\\?.dll" +else + package.path = package.path .. ";../lualib/?.lua" + package.cpath = package.cpath .. ";../luaclib/?.so" end -print(gethttpsize("www.baidu.com")) +print(require "cjson") +print(require "lfs") +print(require "lpeg") +print(require "mime") +print(require "protobuf.c") + + +do return end + +-- local redis = require 'redis' +-- local client = redis.connect("10.4.0.32", 6379, 2) +-- client:set('usr:nrk', "hello world") +-- client:set('usr:nobody', 5) +-- local value1 = client:get('usr:nrk') +-- local value2 = client:get('usr:nobody') +-- print("value", value1, value2) + +-- local http = require 'http' +-- print(http.request("www.baidu.com")) + +-- function gethttpsize(u) + -- local r, c, h = http.request {method = "HEAD", url = u} + -- return r, c, h +-- end + +-- print(gethttpsize("www.baidu.com")) + +--------------------------------- +print("this is the child service : ", context.self, context.parent) +print("context.recv blocking1: ", context.recv(0)) +print("context.recv blocking2: ", context.recv(0)) +local result = { context.recv(0) } +print("context.recv blocking3: ", unpack(result)) +print("context.reply: ", result[2], result[3]) +--timer.sleep(5) +context.reply(result[2], result[3], 1, 2, 3, "aaaa", { ["hello"] = true, }) + +print("context.recv blocking3: ", context.recv(0)) +context.recv(0, function(result, recv_handle, session, ... ) + print("context.recv noblocking: ", result, recv_handle, session, ...) + end +) + diff --git a/sample/test_http.lua b/sample/test_http.lua new file mode 100644 index 0000000..613b8bf --- /dev/null +++ b/sample/test_http.lua @@ -0,0 +1,21 @@ +-- if context.winos then + -- package.cpath = package.cpath ..";".."..\\clib\\?.dll" + -- package.path = package.path ..";".."..\\lualib\\?.lua" +-- else + -- package.cpath = package.cpath ..";".."../clib/?.so" + -- package.path = package.path ..";".."../lualib/?.lua" +-- end + +if context.winos then + package.cpath = package.cpath ..";".."clib\\?.dll" + package.path = package.path ..";".."lualib\\?.lua" +else + package.cpath = package.cpath ..";".."clib/?.so" + package.path = package.path ..";".."lualib/?.lua" +end + +local http = require "http" +local a, b = http.request("http://www.baidu.com") +for k, v in pairs(b) do + print(k, v) +end \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index 1cb96f7..48f6a26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,9 +20,9 @@ LIBRARY_PATH := ../deps/lua ../deps/uv LIBS := nlua uv m ## define CFLAGS -CFLAGS += -g -pedantic -O3 -Wall -Wextra -Wno-unused-parameter -D LUA_COMPAT_ALL +CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -D LUA_COMPAT_5_2 -D LUA_COMPAT_5_1 ifeq (RELEASE,$(RELEASE)) -CFLAGS += -D RELEASE +CFLAGS += -D RELEASE -O3 endif ## define LDFLAGS @@ -47,11 +47,11 @@ LDFLAGS += -undefined dynamic_lookup \ endif ifeq (linux,$(PLATFORM)) -LIBS += dl rt +LIBS += dl rt pthread endif ifeq (freebsd,$(PLATFORM)) -ifeq ($(shell dtrace -l 1>&2 2>/dev/null; echo $$?),0) +ifeq ($(shell dtrace -l >/dev/null; echo $$?),0) HAVE_DTRACE ?= 1 endif LIBS += kvm pthread @@ -69,10 +69,9 @@ ifeq (openbsd,$(PLATFORM)) LIBS += kvm endif -## here we must include ../deps/uv/unix (include dtrace header file) and declare HAVE_DTRACE macro. -## otherwise dtrace method will be undefined reference linkage. +## !!! HAVE_DTRACE not supported(undefined reference to `__dtrace_uv___tick__start' & undefined reference to `__dtrace_uv___tick__stop') ifeq ($(HAVE_DTRACE), 1) -INCLUDE_PATH += ../deps/uv/unix +INCLUDE_PATH += ../deps/uv/src/unix CFLAGS += -DHAVE_DTRACE endif diff --git a/src/buffer.cpp b/src/buffer.cpp index 0b4bd39..5ab03ea 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -47,7 +47,7 @@ bool buffer_append(buffer_t& buffer_ref, const char *data, size_t data_len) { if (buffer && data) { if (buffer->m_ref == 1) { if (data_len <= (size_t)(buffer->m_cap - buffer->m_len)) { - memcpy(buffer->m_data + buffer->m_len, data, data_len); + memmove(buffer->m_data + buffer->m_len, data, data_len); buffer->m_len += data_len; buffer->m_data[buffer->m_len] = 0; return true; @@ -62,8 +62,8 @@ bool buffer_append(buffer_t& buffer_ref, const char *data, size_t data_len) { if (new_buffer) { if (offset >= 0) data = (char*)new_buffer + offset; new_buffer->m_cap = new_cap; - new_buffer->m_len = buffer->m_len + data_len; - memcpy(new_buffer->m_data + buffer->m_len, data, data_len); + memmove(new_buffer->m_data + new_buffer->m_len, data, data_len); + new_buffer->m_len += data_len; new_buffer->m_data[new_buffer->m_len] = 0; buffer_ref.m_ptr = new_buffer; return true; @@ -92,6 +92,87 @@ bool buffer_append(buffer_t& buffer_ref, const char *data, size_t data_len) { return buffer ? true : false; } +char* buffer_reserve(buffer_t& buffer_ref, size_t data_len) { + buffer_data_ptr_t buffer = buffer_ref.m_ptr; + if (buffer) { + if (buffer->m_ref == 1) { + if (data_len <= (size_t)(buffer->m_cap - buffer->m_len)) { + return buffer->m_data + buffer->m_len; + } + size_t new_cap = buffer_gen_capacity(buffer_ref, data_len); + if (new_cap > 0) { + buffer_data_ptr_t new_buffer = (buffer_data_ptr_t)nl_realloc(buffer, sizeof(buffer_data_t) + new_cap + 1); + if (new_buffer) { + new_buffer->m_cap = new_cap; + buffer_ref.m_ptr = new_buffer; + return new_buffer->m_data + new_buffer->m_len; + } + } + return NULL; + } else { + size_t new_cap = buffer_gen_capacity(buffer_ref, data_len); + if (new_cap > 0) { + buffer_data_ptr_t new_buffer = (buffer_data_ptr_t)nl_malloc(sizeof(buffer_data_t) + new_cap + 1); + if (new_buffer) { + new_buffer->m_cap = new_cap; + new_buffer->m_ref = 1; + new_buffer->m_len = buffer->m_len; + memcpy(new_buffer->m_data, buffer->m_data, buffer->m_len); + new_buffer->m_data[new_buffer->m_len] = 0; + buffer_release(buffer_ref); + buffer_ref.m_ptr = new_buffer; + return new_buffer->m_data + new_buffer->m_len; + } + } + return NULL; + } + } + return NULL; +} + +bool buffer_append_char(buffer_t& buffer_ref, char ch) { + buffer_data_ptr_t buffer = buffer_ref.m_ptr; + if (buffer) { + if (buffer->m_ref == 1) { + if (1 <= (size_t)(buffer->m_cap - buffer->m_len)) { + buffer->m_data[buffer->m_len] = ch; + buffer->m_data[++buffer->m_len] = 0; + return true; + } + size_t new_cap = buffer_gen_capacity(buffer_ref, 1); + if (new_cap > 0) { + buffer_data_ptr_t new_buffer = (buffer_data_ptr_t)nl_realloc(buffer, sizeof(buffer_data_t) + new_cap + 1); + if (new_buffer) { + new_buffer->m_cap = new_cap; + new_buffer->m_data[new_buffer->m_len] = ch; + new_buffer->m_data[++new_buffer->m_len] = 0; + buffer_ref.m_ptr = new_buffer; + return true; + } + } + return false; + } else { + size_t new_cap = buffer_gen_capacity(buffer_ref, 1); + if (new_cap > 0) { + buffer_data_ptr_t new_buffer = (buffer_data_ptr_t)nl_malloc(sizeof(buffer_data_t) + new_cap + 1); + if (new_buffer) { + new_buffer->m_cap = new_cap; + new_buffer->m_ref = 1; + new_buffer->m_len = buffer->m_len; + memcpy(new_buffer->m_data, buffer->m_data, buffer->m_len); + new_buffer->m_data[new_buffer->m_len] = ch; + new_buffer->m_data[++new_buffer->m_len] = 0; + buffer_release(buffer_ref); + buffer_ref.m_ptr = new_buffer; + return true; + } + } + return false; + } + } + return false; +} + bool buffer_clear(buffer_t& buffer_ref) { buffer_data_ptr_t buffer = buffer_ref.m_ptr; if (!buffer) return true; @@ -156,7 +237,7 @@ buffer_t buffer_split(buffer_t& buffer_ref, size_t len) if (buffer->m_ref == 1) { buffer->m_len -= split_len; if (buffer->m_len > 0) { - memcpy(buffer->m_data, buffer->m_data + split_len, buffer->m_len); + memmove(buffer->m_data, buffer->m_data + split_len, buffer->m_len); } buffer->m_data[buffer->m_len] = 0; } else { @@ -165,6 +246,7 @@ buffer_t buffer_split(buffer_t& buffer_ref, size_t len) buffer_release(buffer_ref); buffer_ref = new_buffer; } else { //split_len == buffer->m_len + buffer_release(buffer_ref); buffer_ref = buffer_new(DEFAULT_BUFFER_CAPACITY, NULL, 0); } } @@ -173,3 +255,40 @@ buffer_t buffer_split(buffer_t& buffer_ref, size_t len) } return buffer_new(DEFAULT_BUFFER_CAPACITY, NULL, 0); } + +size_t buffer_remove(buffer_t& buffer_ref, size_t start, size_t len) +{ + buffer_data_ptr_t buffer = buffer_ref.m_ptr; + if (buffer && len > 0 && (size_t)buffer->m_len > start) { + int32_t remove_len = len < ((size_t)buffer->m_len - start) ? (int32_t)len : ((size_t)buffer->m_len - start); + if (buffer->m_ref == 1) { + buffer->m_len -= remove_len; + if ((size_t)buffer->m_len > start) { + memmove(buffer->m_data + start, buffer->m_data + start + remove_len, buffer->m_len - start); + } + buffer->m_data[buffer->m_len] = 0; + } else { + if (remove_len < buffer->m_len) { + buffer_t new_buffer = buffer_new(buffer->m_len - remove_len, NULL, 0); + buffer_data_ptr_t nbuffer = new_buffer.m_ptr; + if (nbuffer) { + nbuffer->m_len = buffer->m_len - remove_len; + if (start > 0) { + memcpy(nbuffer->m_data, buffer->m_data, start); + } + if ((size_t)nbuffer->m_len > start) { + memcpy(nbuffer->m_data + start, buffer->m_data + start + remove_len, nbuffer->m_len - start); + } + nbuffer->m_data[nbuffer->m_len] = 0; + } + buffer_release(buffer_ref); + buffer_ref = new_buffer; + } else { //remove_len == buffer->m_len + buffer_release(buffer_ref); + buffer_ref = buffer_new(DEFAULT_BUFFER_CAPACITY, NULL, 0); + } + } + return remove_len; + } + return 0; +} diff --git a/src/buffer.h b/src/buffer.h index 65f4f0f..ebba85e 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -19,12 +19,15 @@ typedef struct { extern buffer_t buffer_new(size_t cap, const char *init, size_t init_len); extern bool buffer_append(buffer_t& buffer_ref, const char *data, size_t data_len); +extern char* buffer_reserve(buffer_t& buffer_ref, size_t data_len); +extern bool buffer_append_char(buffer_t& buffer_ref, char ch); extern bool buffer_clear(buffer_t& buffer_ref); extern buffer_t& buffer_grab(buffer_t& buffer_ref); extern void buffer_release(buffer_t& buffer_ref); extern bool buffer_concat(buffer_t& dest_ref, const buffer_t& src_ref); extern bool buffer_adjust_len(buffer_t& buffer_ref, size_t add); extern buffer_t buffer_split(buffer_t& buffer_ref, size_t len); +extern size_t buffer_remove(buffer_t& buffer_ref, size_t start, size_t len); #define invalid_buffer { NULL } #define buffer_is_valid(buffer_ref) ((buffer_ref).m_ptr != NULL) diff --git a/src/common.h b/src/common.h index 32625e1..1716ba2 100644 --- a/src/common.h +++ b/src/common.h @@ -6,7 +6,13 @@ #include #include "config.h" -#ifdef WIN32 +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(WINCE) || defined(_WIN32_WCE) || defined(_WIN64) +#ifndef CC_MSVC +#define CC_MSVC +#endif +#endif + +#ifdef CC_MSVC #define FORCE_INLINE __forceinline #else #define FORCE_INLINE inline @@ -17,7 +23,6 @@ #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - offsetof(type, member))) -//to be fix : fix me #ifdef RELEASE #define WPAssert( assertion ) { if( !(assertion) ) { fprintf( stderr, "\n%s:%i ASSERTION FAILED:\n %s\n", __FILE__, __LINE__, #assertion ); } } #else @@ -37,13 +42,9 @@ #include "latomic.h" #include "utils.h" #include "errors.h" +#include "nlmalloc.h" #define nl (singleton_ref(node_lua_t)) -#define nl_malloc malloc -#define nl_free free -#define nl_calloc calloc -#define nl_realloc realloc -#define nl_strdup strdup #if defined __GNUC__ #define likely(x) __builtin_expect ((x), 1) diff --git a/src/context.cpp b/src/context.cpp index 9c93054..5f456de 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -5,7 +5,8 @@ #include "node_lua.h" context_t::context_t() - : m_handle(0), + : m_parent(0), + m_handle(0), m_worker(NULL), m_inited(false), m_ref(1), @@ -20,9 +21,10 @@ context_t::~context_t() } -void context_t::on_registered( uint32_t handle ) +void context_t::on_registered(uint32_t parent, uint32_t handle) { if (m_handle == 0) { + m_parent = parent; m_handle = handle; grab(); } diff --git a/src/context.h b/src/context.h index 1d1c267..bf9cd5c 100644 --- a/src/context.h +++ b/src/context.h @@ -8,6 +8,7 @@ class worker_t; class context_t { public: context_t(); + FORCE_INLINE uint32_t get_parent() const { return m_parent; } FORCE_INLINE uint32_t get_handle() const { return m_handle; } FORCE_INLINE void grab() { atomic_inc(m_ref); } FORCE_INLINE void release() { if (atomic_dec(m_ref) == 1) delete this; } @@ -22,7 +23,7 @@ class context_t { void attach_worker(worker_t* worker); void detach_worker(); - void on_registered(uint32_t handle); /* thread safe */ + void on_registered(uint32_t parent, uint32_t handle); /* thread safe */ void on_retired(); /* thread safe */ bool push_message(message_t& message, bool& processing); bool pop_message(message_t& message); @@ -39,6 +40,7 @@ class context_t { context_t(const context_t& ctx); context_t& operator=(const context_t& ctx); + uint32_t m_parent; /* parent handle */ uint32_t m_handle; /* allocated handle */ worker_t* m_worker; /* attached worker */ private: diff --git a/src/context_log.cpp b/src/context_log.cpp new file mode 100644 index 0000000..0875512 --- /dev/null +++ b/src/context_log.cpp @@ -0,0 +1,58 @@ +#include "context_log.h" +#include "node_lua.h" + +bool context_log_t::init(int32_t argc, char* argv[], char* env[]) +{ + return true; +} + +bool context_log_t::deinit(const char *arg) +{ + return true; +} + +void context_log_t::on_received(message_t& message) +{ + switch (message_raw_type(message)) { + case LOG_MESSAGE: + log_message(message); + return; + case SYSTEM_CTX_DESTROY: + singleton_ref(node_lua_t).context_destroy(this, message.m_source, message_string(message)); + return; + default: + break; + } +} + +void context_log_t::on_dropped(message_t& message) +{ + switch (message_raw_type(message)) { + case LOG_MESSAGE: + log_message(message); + return; + default: + break; + } +} + +void context_log_t::log_message(message_t& message) +{ + switch (message_data_type(message)) { + case BINARY: + if (message_binary(message).m_data) { + lua_writestring(message_binary(message).m_data, sdslen(message_binary(message).m_data)); + lua_writeline(); + } + break; + case STRING: + if (message_string(message)) { + lua_writestring(message_string(message), strlen(message_string(message))); + lua_writeline(); + } + break; + default: + break; + } +} + diff --git a/src/context_log.h b/src/context_log.h new file mode 100644 index 0000000..6dc06d7 --- /dev/null +++ b/src/context_log.h @@ -0,0 +1,18 @@ +#ifndef CONTEXT_LOG_H_ +#define CONTEXT_LOG_H_ + +#include "common.h" +#include "request.h" +#include "context.h" + +class context_log_t : public context_t { +public: + bool init(int32_t argc, char* argv[], char* env[]); + bool deinit(const char *arg); + void on_received(message_t& message); + void on_dropped(message_t& message); +private: + void log_message(message_t& message); +}; + +#endif \ No newline at end of file diff --git a/src/context_lua.cpp b/src/context_lua.cpp index d78515f..f1a4056 100644 --- a/src/context_lua.cpp +++ b/src/context_lua.cpp @@ -1,12 +1,14 @@ #include "common.h" #include "utils.h" #include "lbuffer.h" +#include "lshared.h" #include "context.h" #include "context_lua.h" #include "node_lua.h" #include "worker.h" #include "uv_handle_base.h" #include "lua_tcp_handle.h" +#include "lua_udp_handle.h" #include "lua_timer_handle.h" #include "errors.h" @@ -16,7 +18,8 @@ char context_lua_t::m_ref_session_table_key = 0; char context_lua_t::m_ref_timer_table_key = 0; context_lua_t::context_lua_t() - : m_lstate(NULL), + : m_name(NULL), + m_lstate(NULL), m_ref_session_count(0), m_ref_session_base(INT_MAX) { @@ -28,18 +31,22 @@ context_lua_t::~context_lua_t() lua_close(m_lstate); m_lstate = NULL; } + if (m_name) { + nl_free((void*)m_name); + m_name = NULL; + } } bool context_lua_t::init(int32_t argc, char* argv[], char* env[]) { m_lstate = luaL_newstateex(this); if (argc <= 0) { - printf("[error] context:0x%08x init failed: lua file is needed to initialize lua context\n", m_handle); /* to be implemented : put reason to another context and output it */ + singleton_ref(node_lua_t).log_fmt(m_handle, "[error] context:0x%08x init failed: lua file is needed to initialize lua context", m_handle); return false; } int32_t envc = 2; if (argc > MAX_CTX_ARGC || !lua_checkstack(m_lstate, argc + envc)) { - printf("[error] context:0x%08x init failed: too many arguments to initialize lua context %s\n", m_handle, argv[0]); /* to be implemented : put reason to another context and output it */ + singleton_ref(node_lua_t).log_fmt(m_handle, "[error] context:0x%08x init failed: too many arguments to initialize lua context %s", m_handle, argv[0]); return false; } int32_t total_length = 0; @@ -62,16 +69,16 @@ bool context_lua_t::init(int32_t argc, char* argv[], char* env[]) } lua_pushstring(m_lstate, lua_path); lua_pushstring(m_lstate, lua_cpath); - char* args = (char*)nl_calloc(total_length, 1); + char* args = (char*)nl_calloc(total_length); char* argp = args; for (int32_t i = 0; i < argc; ++i) { memcpy(argp, argv[i], lengths[i]); argp += lengths[i]; *argp++ = (i != argc - 1) ? ' ' : '\0'; } - printf("[alert] context:0x%08x init: %s\n", m_handle, args); /* to be implemented : put reason to another context and output it */ - nl_free(args); - return singleton_ref(node_lua_t).context_send(this, m_handle, 0, LUA_CTX_INIT, (double)argc); + m_name = args; + singleton_ref(node_lua_t).log_fmt(m_handle, "[alert] context:0x%08x init: %s", m_handle, args); + return singleton_ref(node_lua_t).context_send(this, m_handle, 0, LUA_CTX_INIT, (int64_t)argc); } bool context_lua_t::deinit(const char *arg) @@ -80,11 +87,20 @@ bool context_lua_t::deinit(const char *arg) singleton_ref(node_lua_t).context_send(*it, m_handle, LUA_REFNIL, LUA_CTX_WAKEUP, UV_OK); } m_context_wait_handles.clear(); + + for (ref_sessions_mgr_t::ref_sessions_map_t::iterator it = m_context_wait_sessions.m_sessions_map.begin(); it != m_context_wait_sessions.m_sessions_map.end(); ++it) { + singleton_ref(node_lua_t).context_send(it->first, m_handle, LUA_REFNIL, LUA_CTX_WAIT_CANCEL, UV_OK); + } + if (m_lstate) { lua_close(m_lstate); m_lstate = NULL; } - printf("[alert] context:0x%08x deinit: %s\n", m_handle, arg); /* to be implemented : put reason to another context and output it */ + if (m_name) { + nl_free((void*)m_name); + m_name = NULL; + } + singleton_ref(node_lua_t).log_fmt(m_handle, "[alert] context:0x%08x deinit: %s", m_handle, arg); return true; } @@ -108,6 +124,8 @@ void context_lua_t::on_worker_detached() LUAMOD_API int (luaopen_my_coroutine)(lua_State *L); #define LUA_TCPLIBNAME "tcp" LUAMOD_API int (luaopen_tcp)(lua_State *L); +#define LUA_UDPLIBNAME "udp" +LUAMOD_API int (luaopen_udp)(lua_State *L); #define LUA_BUFFERLIBNAME "buffer" LUAMOD_API int (luaopen_buffer)(lua_State *L); #define LUA_CONTEXTLIBNAME "context" @@ -127,11 +145,13 @@ void context_lua_t::lua_open_libs(lua_State *L) {LUA_STRLIBNAME, luaopen_string}, {LUA_BITLIBNAME, luaopen_bit32}, {LUA_MATHLIBNAME, luaopen_math}, + {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, #if defined(LUA_CACHELIB) {LUA_CACHELIB, luaopen_cache}, #endif {LUA_TCPLIBNAME, luaopen_tcp}, + {LUA_UDPLIBNAME, luaopen_udp}, {LUA_BUFFERLIBNAME, luaopen_buffer}, {LUA_CONTEXTLIBNAME, luaopen_context}, {LUA_TIMERLIBNAME, luaopen_timer}, @@ -155,6 +175,13 @@ void context_lua_t::lua_open_libs(lua_State *L) lua_pop(L, 1); /* remove _PRELOAD table */ } +void context_lua_t::unload() +{ +#if defined(LUA_CACHELIB) + luaL_closesharedclosure(); +#endif +} + void context_lua_t::lua_load_env(lua_State *L, char* env[]) { lua_checkstack(L, 3); @@ -331,16 +358,17 @@ int32_t context_lua_t::yielding_finalize(lua_State *root_coro, int32_t status) return m_shared->m_yielding_status; } -int32_t context_lua_t::common_yield_continue(lua_State* L) +int32_t context_lua_t::common_yield_continue(lua_State* L, int status, lua_KContext ctx) { - if (!lua_toboolean(L, -4) && context_lua_t::lua_get_context(L)->get_yielding_status() != UV_OK) { - context_lua_t::lua_throw(L, -3); + int32_t old_top = (int32_t)ctx; + if (!lua_toboolean(L, old_top + 1) && context_lua_t::lua_get_context(L)->get_yielding_status() != UV_OK) { + context_lua_t::lua_throw(L, old_top + 2); } lua_pop(L, 2); //pop out two useless arguments - return 2; + return lua_gettop(L) - old_top; } -int32_t context_lua_t::lua_yield_send(lua_State *L, uint32_t destination, yiled_finalize_t fnz, void* fnz_ud, lua_CFunction yieldk, int64_t timeout) +int32_t context_lua_t::lua_yield_send(lua_State *L, uint32_t destination, yiled_finalize_t fnz, void* fnz_ud, lua_KFunction yieldk, int64_t timeout) { int stacks; context_lua_t* lctx = context_lua_t::lua_get_context(L); @@ -350,16 +378,17 @@ int32_t context_lua_t::lua_yield_send(lua_State *L, uint32_t destination, yiled_ lctx->m_shared->m_yielding_finalize_userdata = fnz_ud; lctx->m_shared->m_yielding_status = UV_OK; lctx->m_shared->m_yielding_timeout = timeout; - if ((stacks = lua_checkstack(L, LUA_MINSTACK)) && lua_yieldable(L)) { /* reserve LUA_MINSTACK stack for resume result */ - return lua_yieldk(L, 0, 0, yieldk); /* n(0) result to yield and ctx(0) is not allowed */ + if ((stacks = lua_checkstack(L, LUA_MINSTACK)) && lua_isyieldable(L)) { /* reserve LUA_MINSTACK stack for resume result */ + return lua_yieldk(L, 0, (lua_KContext)lua_gettop(L), yieldk); } else { if (stacks) { /* stack enough, but not yield-able */ lctx->yielding_finalize(NULL, NL_EYIELD); + int32_t top = lua_gettop(L); lua_pushboolean(L, 0); lua_pushinteger(L, NL_EYIELD); lua_pushnil(L); lua_pushnil(L); - return (yieldk == NULL) ? 4 : yieldk(L); + return (yieldk == NULL) ? 4 : yieldk(L, LUA_YIELD, (lua_KContext)top); } else { /* error jump directly */ lctx->yielding_finalize(NULL, NL_ESTACKLESS); return luaL_error(L, "attempt to yield across a stack-less coroutine"); @@ -378,18 +407,31 @@ int32_t context_lua_t::lua_ref_callback_entry_finish(lua_State *L, int32_t statu if (!status) { /* error message is at top */ uint32_t handle = lua_get_context_handle(L); const char* error = lua_tostring(L, -1); - /* to be implemented : put reason to another context and output it */ - printf("[error] context:0x%08x error: %s\n", handle, error ? error : "unknown error"); + singleton_ref(node_lua_t).log_fmt(handle, "[error] context:0x%08x error: %s", handle, error ? error : "unknown error"); } return 0; //return none result out!!! } -int32_t context_lua_t::lua_ref_callback_entry_continue(lua_State *L) +int32_t context_lua_t::lua_ref_callback_entry_continue(lua_State *L, int status, lua_KContext ctx) { - int status = lua_getctx(L, NULL); return lua_ref_callback_entry_finish(L, (status == LUA_YIELD)); } +int32_t context_lua_t::lua_ref_callback_error_handler(lua_State *L) +{ + const char *msg = lua_tostring(L, 1); + if (msg == NULL) { /* is error object not a string? */ + if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ + lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ + return 1; /* that is the message */ + else + msg = lua_pushfstring(L, "(error object is a %s value)", + luaL_typename(L, 1)); + } + luaL_traceback(L, L, msg, 1); /* append a standard traceback */ + return 1; /* return the traceback */ +} + int32_t context_lua_t::lua_ref_callback_entry(lua_State *L) { int upvalue_count = 0; @@ -402,15 +444,17 @@ int32_t context_lua_t::lua_ref_callback_entry(lua_State *L) adjust_resume_args(L); } --upvalue_count; - int resume_count = lua_gettop(L); - lua_checkstack(L, upvalue_count); + int resume_count = lua_gettop(L); /* record resume arg count */ + lua_checkstack(L, upvalue_count + 1); /* reserve `upvalue_count + 1` stack size */ + lua_pushcclosure(L, lua_ref_callback_error_handler, 0); + lua_insert(L, 1); /* put error handler function at bottom (the `1st` arg) */ lua_pushvalue(L, lua_upvalueindex(upvalue_count)); - lua_insert(L, 1); /* put callback closure as first arg */ - for (int i = 1; i < upvalue_count; ++i) { + lua_insert(L, 2); /* put callback closure as the first arg behind error handler */ + for (int i = 1; i < upvalue_count; ++i) { /* put `upvalue_count - 1` args */ lua_pushvalue(L, lua_upvalueindex(i)); } /* lua_callback_entry_continue makes the coroutine can be recovered. */ - int status = lua_pcallk(L, upvalue_count + resume_count - 1, LUA_MULTRET, 0, 0, lua_ref_callback_entry_continue); + int status = lua_pcallk(L, upvalue_count + resume_count - 1, LUA_MULTRET, 1, 0, lua_ref_callback_entry_continue); return lua_ref_callback_entry_finish(L, (status == LUA_OK)); } @@ -525,28 +569,60 @@ void context_lua_t::lua_free_ref_session(lua_State *L, int32_t ref) void context_lua_t::lua_pushmessage(lua_State *L, message_t& message) { + message_array_t *array = NULL; switch (message_data_type(message)) { - case NIL: + case NIL : lua_pushnil(L); return; - case TBOOLEAN: + case TBOOLEAN : lua_pushboolean(L, message_bool(message)); return; - case USERDATA: + case USERDATA : lua_pushlightuserdata(L, message_userdata(message)); return; - case NUMBER: + case NUMBER : lua_pushnumber(L, message_number(message)); return; - case BUFFER: + case INTEGER : + lua_pushinteger(L, message_integer(message)); + return; + case BUFFER : create_buffer(L, message_buffer(message)); return; - case STRING: - lua_pushstring(L, message_string(message)); + case SHARED : + shared_t::create(L, message_shared(message)); + return; + case STRING : + if (message_string(message)) { + lua_pushstring(L, message_string(message)); + } else { + lua_pushstring(L, ""); + } + return; + case BINARY : + if (message_binary(message).m_data) { + lua_pushlstring(L, message_binary(message).m_data, sdslen(message_binary(message).m_data)); + } else { + lua_pushstring(L, ""); + } + return; + case TPACK : + ltunpack(&message_tpack(message), L); return; - case TERROR: + case TERROR : lua_pushinteger(L, message_error(message)); return; + case ARRAY : + array = message_array(message); + if (array != NULL && array->m_count > 0) { + lua_checkstack(L, array->m_count); + for (uint32_t i = 0; i < array->m_count; ++i) { //extract them + lua_pushmessage(L, array->m_array[i]); + } + } else { + lua_pushnil(L); + } + return; default: lua_pushnil(L); return; @@ -595,11 +671,15 @@ int32_t context_lua_t::wakeup_ref_session(int32_t ref, message_t& message, bool if (blocking || free_callback) { lua_free_ref_session(m_lstate, ref); } + int32_t oldTop = lua_gettop(co); + lua_checkstack(co, 2); lua_pushboolean(co, !message_is_pure_error(message)); lua_pushmessage(co, message); - lua_pushnumber(co, message.m_source); + lua_checkstack(co, 2); + lua_pushinteger(co, message.m_source); lua_pushinteger(co, message.m_session); - resume_coroutine(co, 4); + int32_t newTop = lua_gettop(co); + resume_coroutine(co, newTop - oldTop); return 1; } return 0; @@ -621,9 +701,15 @@ void context_lua_t::on_received(message_t& message) case LUA_CTX_WAIT: response_context_wait(message); break; + case LUA_CTX_WAIT_CANCEL: + response_context_wait_cancel(message); + break; case LUA_CTX_WAKEUP: response_context_wakeup(message); break; + case LUA_CTX_RUN: + response_context_run(message); + break; case RESPONSE_TCP_LISTEN: response_tcp_listen(message); break; @@ -639,12 +725,24 @@ void context_lua_t::on_received(message_t& message) case RESPONSE_TCP_WRITE: response_tcp_write(message); break; - case RESPONSE_HANDLE_CLOSE: - response_handle_close(message); - break; case RESPONSE_TCP_CLOSING: response_tcp_closing(message); break; + case RESPONSE_UDP_OPEN: + response_udp_open(message); + break; + case RESPONSE_UDP_WRITE: + response_udp_write(message); + break; + case RESPONSE_UDP_READ: + response_udp_read(message); + break; + case RESPONSE_UDP_CLOSING: + response_udp_closing(message); + break; + case RESPONSE_HANDLE_CLOSE: + response_handle_close(message); + break; case RESPONSE_TIMEOUT: response_timeout(message); break; @@ -665,6 +763,7 @@ void context_lua_t::on_dropped(message_t& message) case RESPONSE_TCP_LISTEN: case RESPONSE_TCP_ACCEPT: case RESPONSE_TCP_CONNECT: + case RESPONSE_UDP_OPEN: if (message_is_userdata(message)) { uv_handle_base_t* handle = (uv_handle_base_t*)message_userdata(message); lua_handle_base_t::close_uv_handle(handle); @@ -676,7 +775,7 @@ void context_lua_t::on_dropped(message_t& message) } return; case LUA_CTX_WAIT: - singleton_ref(node_lua_t).context_send(message.m_source, (uint32_t)message_number(message), LUA_REFNIL, LUA_CTX_WAKEUP, UV_OK); + singleton_ref(node_lua_t).context_send(message.m_source, (uint32_t)message_integer(message), LUA_REFNIL, LUA_CTX_WAKEUP, UV_OK); return; default: break; @@ -685,7 +784,7 @@ void context_lua_t::on_dropped(message_t& message) void context_lua_t::lua_ctx_init(message_t& message) { - int32_t argc = (int32_t)message_number(message); + int32_t argc = (int32_t)message_integer(message); int32_t envc = lua_gettop(m_lstate) - argc; const char *file = lua_tostring(m_lstate, 1); if (luaL_loadfile(m_lstate, file) == LUA_OK) { @@ -728,11 +827,21 @@ void context_lua_t::response_context_wait(message_t& response) m_context_wait_handles.insert(response.m_source); } +void context_lua_t::response_context_wait_cancel(message_t& response) +{ + m_context_wait_handles.erase(response.m_source); +} + void context_lua_t::response_context_wakeup(message_t& response) { m_context_wait_sessions.wakeup_all(response.m_source, this, response, true); } +void context_lua_t::response_context_run(message_t& response) +{ + wakeup_ref_session(response.m_session, response, true); +} + void context_lua_t::response_tcp_listen(message_t& response) { lua_tcp_listen_handle_t::wakeup_listen(m_lstate, response); @@ -758,11 +867,6 @@ void context_lua_t::response_tcp_read(message_t& response) lua_tcp_socket_handle_t::wakeup_read(m_lstate, response); } -void context_lua_t::response_handle_close(message_t& response) -{ - lua_handle_base_t::release_ref(m_lstate, response.m_session, (handle_set)(int32_t)message_number(response)); -} - void context_lua_t::response_tcp_closing(message_t& response) { if (message_error(response) == NL_ETCPLCLOSED) { /* listen socket */ @@ -772,6 +876,31 @@ void context_lua_t::response_tcp_closing(message_t& response) } } +void context_lua_t::response_udp_open(message_t& response) +{ + lua_udp_handle_t::wakeup_open(m_lstate, response); +} + +void context_lua_t::response_udp_write(message_t& response) +{ + wakeup_ref_session(response.m_session, response, true); +} + +void context_lua_t::response_udp_read(message_t& response) +{ + lua_udp_handle_t::wakeup_read(m_lstate, response); +} + +void context_lua_t::response_udp_closing(message_t& response) +{ + lua_udp_handle_t::wakeup_read(m_lstate, response); +} + +void context_lua_t::response_handle_close(message_t& response) +{ + lua_handle_base_t::release_ref(m_lstate, response.m_session, (handle_set)(int32_t)message_integer(response)); +} + void context_lua_t::response_timeout(message_t& response) { lua_timer_handle_t::wakeup(m_lstate, response); @@ -799,7 +928,8 @@ int32_t context_lua_t::context_create(lua_State *L) for (int32_t i = 0; i < argc; ++i) { argv[i] = (char*)luaL_checkstring(L, i + 1); } - uint32_t handle = singleton_ref(node_lua_t).context_create(argc, argv, env); + uint32_t parent = lua_get_context_handle(L); + uint32_t handle = singleton_ref(node_lua_t).context_create(parent, argc, argv, env); lua_free_env(L, env); if (handle > 0) { lua_pushinteger(L, handle); @@ -812,103 +942,137 @@ int32_t context_lua_t::context_destroy(lua_State *L) { int32_t type = lua_type(L, 1); uint32_t src_handle = lua_get_context_handle(L); - if (type == LUA_TNIL || type == LUA_TSTRING) { //kill self + if (type == LUA_TNONE || type == LUA_TNIL || type == LUA_TSTRING) { //kill self singleton_ref(node_lua_t).context_destroy(src_handle, src_handle, lua_tostring(L, 1)); return 0; } - int32_t handle = luaL_checkunsigned(L, 1); - if (handle > 0) { - singleton_ref(node_lua_t).context_destroy(handle, src_handle, lua_tostring(L, 2)); + uint32_t handle = luaL_checkunsigned(L, 1); + if (handle == 0 || singleton_ref(node_lua_t).is_handle_illegal(handle)) { + luaL_error(L, "attempt to destroy an illegal context:0x%p", (void*)handle); } + singleton_ref(node_lua_t).context_destroy(handle, src_handle, lua_tostring(L, 2)); return 0; } int32_t context_lua_t::context_check_message(lua_State *L, int32_t idx, uint32_t msg_type, message_t& message) { - buffer_t* buffer; + void* udata; + const char* data; + size_t length; switch (lua_type(L, idx)) { case LUA_TNUMBER: - message.m_type = MAKE_MESSAGE_TYPE(msg_type, NUMBER); - message.m_data.m_number = (double)lua_tonumber(L, idx); + if (lua_isinteger(L, idx)) { + message.m_type = MAKE_MESSAGE_TYPE(msg_type, INTEGER); + message.m_data.m_integer = (int64_t)lua_tointeger(L, idx); + } else { + message.m_type = MAKE_MESSAGE_TYPE(msg_type, NUMBER); + message.m_data.m_number = (double)lua_tonumber(L, idx); + } message.m_source = lua_get_context_handle(L); - return UV_OK; + return 1; case LUA_TSTRING: - message.m_type = MAKE_MESSAGE_TYPE(msg_type, STRING); - message.m_data.m_string = (char*)lua_tostring(L, idx); + data = lua_tolstring(L, idx, &length); + message.m_type = MAKE_MESSAGE_TYPE(msg_type, BINARY); + message.m_data.m_binary.m_data = sdsnewlen(data, length); message.m_source = lua_get_context_handle(L); - return UV_OK; + return 1; case LUA_TBOOLEAN: message.m_type = MAKE_MESSAGE_TYPE(msg_type, TBOOLEAN); message.m_data.m_bool = (bool)lua_toboolean(L, idx); message.m_source = lua_get_context_handle(L); - return UV_OK; + return 1; case LUA_TNIL: message.m_type = MAKE_MESSAGE_TYPE(msg_type, NIL); message.m_source = lua_get_context_handle(L); - return UV_OK; + return 1; case LUA_TUSERDATA: - buffer = (buffer_t*)luaL_testudata(L, idx, BUFFER_METATABLE); - if (!buffer) return NL_ETRANSTYPE; - message.m_type = MAKE_MESSAGE_TYPE(msg_type, USERDATA); - message.m_data.m_buffer = *buffer; - message.m_source = lua_get_context_handle(L); - return UV_OK; + udata = lua_touserdata(L, idx); + if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ + luaL_getmetatable(L, BUFFER_METATABLE); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* not the same? */ + lua_pop(L, 2); /* remove both metatables */ + message.m_type = MAKE_MESSAGE_TYPE(msg_type, BUFFER); + message.m_data.m_buffer = buffer_grab(*((buffer_t*)udata)); + message.m_source = lua_get_context_handle(L); + return 1; + } + lua_pop(L, 1); /* remove BUFFER_METATABLE */ + luaL_getmetatable(L, SHARED_METATABLE); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* not the same? */ + lua_pop(L, 2); /* remove both metatables */ + message.m_type = MAKE_MESSAGE_TYPE(msg_type, SHARED); + message.m_data.m_shared = *(shared_t**)udata ? (*(shared_t**)udata)->grab() : NULL; + message.m_source = lua_get_context_handle(L); + return 1; + } + lua_pop(L, 2); /* remove both metatables */ + } + lua_pushstring(L, "transfer data type not supported"); + return 0; case LUA_TLIGHTUSERDATA: message.m_type = MAKE_MESSAGE_TYPE(msg_type, USERDATA); message.m_data.m_userdata = (void*)lua_touserdata(L, idx); message.m_source = lua_get_context_handle(L); - return UV_OK; + return 1; + case LUA_TTABLE: + message.m_type = MAKE_MESSAGE_TYPE(msg_type, TPACK); + message.m_data.m_tpack.m_data = sdsnewempty(64); + message.m_source = lua_get_context_handle(L); + return ltpack(&message.m_data.m_tpack, L, idx) ? 1 : 0; default: - return NL_ETRANSTYPE; + lua_pushstring(L, "transfer data type not supported"); + return 0; } } -int32_t context_lua_t::context_send(lua_State *L, int32_t idx, uint32_t handle, int32_t session, uint32_t msg_type) +int32_t context_lua_t::context_check_message(lua_State *L, int32_t idx, uint32_t count, uint32_t msg_type, message_t& message) { - bool ret = false; - nil_t nil; - buffer_t* buffer; - context_t* ctx = lua_get_context(L); - switch (lua_type(L, idx)) { - case LUA_TNUMBER: - ret = singleton_ref(node_lua_t).context_send(handle, ctx->get_handle(), session, msg_type, (double)lua_tonumber(L, idx)); - break; - case LUA_TSTRING: - ret = singleton_ref(node_lua_t).context_send_string_safe(handle, ctx->get_handle(), session, msg_type, lua_tostring(L, idx)); - break; - case LUA_TBOOLEAN: - ret = singleton_ref(node_lua_t).context_send(handle, ctx->get_handle(), session, msg_type, (bool)lua_toboolean(L, idx)); - break; - case LUA_TNIL: - ret = singleton_ref(node_lua_t).context_send(handle, ctx->get_handle(), session, msg_type, nil); - break; - case LUA_TUSERDATA: - buffer = (buffer_t*)luaL_testudata(L, idx, BUFFER_METATABLE); - if (buffer) { - ret = singleton_ref(node_lua_t).context_send(handle, ctx->get_handle(), session, msg_type, *buffer); - break; - } else { - return NL_ETRANSTYPE; + if (count <= 1) { + return context_check_message(L, idx, msg_type, message); + } + message_array_t* array = message_array_create(count); + message.m_type = MAKE_MESSAGE_TYPE(msg_type, ARRAY); + message.m_data.m_array = array; + message.m_source = lua_get_context_handle(L); + for (uint32_t i = 0; i < count; ++i) { + if (context_check_message(L, idx + i, msg_type, array->m_array[i]) == 0) { + return 0; } - case LUA_TLIGHTUSERDATA: - ret = singleton_ref(node_lua_t).context_send(handle, ctx->get_handle(), session, msg_type, (void*)lua_touserdata(L, idx)); - break; - default: + } + return 1; +} + +int32_t context_lua_t::context_send(lua_State *L, int32_t idx, uint32_t count, uint32_t handle, int32_t session, uint32_t msg_type) +{ + message_t message; + int32_t status = context_check_message(L, idx, count, msg_type, message); + if (status == 0) { + message_clean(message); return NL_ETRANSTYPE; } - return ret ? UV_OK : NL_ENOCONTEXT; + message.m_session = session; + if (singleton_ref(node_lua_t).context_send(handle, message)) { + return UV_OK; + } + message_clean(message); + return NL_ENOCONTEXT; } int32_t context_lua_t::context_send(lua_State *L) { uint32_t handle = luaL_checkunsigned(L, 1); - int32_t ret = context_send(L, 2, handle, LUA_REFNIL, CONTEXT_QUERY); + if (handle == 0 || singleton_ref(node_lua_t).is_handle_illegal(handle)) { + luaL_error(L, "attempt to send to an illegal context:0x%p", (void*)handle); + } + int32_t top = lua_gettop(L); + luaL_checkany(L, 2); + int32_t ret = context_send(L, 2, top - 1, handle, LUA_REFNIL, CONTEXT_QUERY); if (ret == UV_OK) { lua_pushboolean(L, 1); return 1; } if (ret == NL_ETRANSTYPE) { - luaL_argerror(L, 2, common_strerror(NL_ETRANSTYPE)); + lua_error(L); return 0; } lua_pushboolean(L, 0); @@ -922,12 +1086,7 @@ int32_t context_lua_t::context_query_yield_finalize(lua_State *root_coro, lua_St context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); message_t& message = lctx->get_yielding_message(); message.m_session = context_lua_t::lua_ref_yield_coroutine(root_coro); - bool ret; - if (message_is_string(message)) { - ret = singleton_ref(node_lua_t).context_send_string_safe(destination, lctx->get_handle(), message.m_session, CONTEXT_QUERY, message_string(message)); - } else { - ret = singleton_ref(node_lua_t).context_send(destination, message); - } + bool ret = singleton_ref(node_lua_t).context_send(destination, message); if (ret) { int64_t timeout = lctx->get_yielding_timeout(); if (timeout > 0) { @@ -936,47 +1095,53 @@ int32_t context_lua_t::context_query_yield_finalize(lua_State *root_coro, lua_St return UV_OK; } else { lua_free_ref_session(main_coro, message.m_session); + message_clean(message); return NL_ENOCONTEXT; } + } else { + context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); + message_t& message = lctx->get_yielding_message(); + message_clean(message); } return UV_OK; } -int32_t context_lua_t::context_query_yield_continue(lua_State* L) +int32_t context_lua_t::context_query_yield_continue(lua_State* L, int status, lua_KContext ctx) { - if (!lua_toboolean(L, -4)) { + int32_t old_top = (int32_t)ctx; + if (!lua_toboolean(L, old_top + 1)) { int32_t ret = context_lua_t::lua_get_context(L)->get_yielding_status(); if (ret != UV_OK && ret != NL_ENOCONTEXT) { - context_lua_t::lua_throw(L, -3); + context_lua_t::lua_throw(L, old_top + 2); } } lua_pop(L, 2); //pop two useless arguments - return 2; + return lua_gettop(L) - old_top; } -int32_t context_lua_t::context_query(lua_State *L) +int32_t context_lua_t::context_query(lua_State *L, bool timed_query) { uint32_t handle = luaL_checkunsigned(L, 1); + if (handle == 0 || singleton_ref(node_lua_t).is_handle_illegal(handle)) { + luaL_error(L, "attempt to query to an illegal context:0x%p", (void*)handle); + } int32_t top = lua_gettop(L); uint64_t timeout = 0; - int32_t callback = 0; - if (top >= 3) { //nonblocking - if (lua_isfunction(L, 3)) { - callback = 3; - } else { - timeout = 1000 * luaL_checknumber(L, 3); - if (top >= 4) { - luaL_checktype(L, 4, LUA_TFUNCTION); - callback = 4; - } - } + if (timed_query) { + timeout = 1000 * luaL_checknumber(L, 2); + luaL_checkany(L, 3); + } else { + luaL_checkany(L, 2); } - if (callback > 0) { - lua_settop(L, callback); - lua_pushvalue(L, 2); - lua_insert(L, 1); - int32_t session = context_lua_t::lua_ref_callback(L, callback - 1, LUA_REFNIL, common_callback_adjust); - int32_t ret = context_send(L, 1, handle, session, CONTEXT_QUERY); + bool callback = lua_isfunction(L, -1); + int32_t idx = timed_query ? 3 : 2; + int32_t count = top - 1 - (timed_query ? 1 : 0) - (callback ? 1 : 0); + if (count <= 0) { + luaL_error(L, "no data to be sent"); + } + if (callback) { + int32_t session = context_lua_t::lua_ref_callback(L, 0, LUA_REFNIL, common_callback_adjust); + int32_t ret = context_send(L, idx, count, handle, session, CONTEXT_QUERY); if (ret == UV_OK) { if (timeout > 0) { context_lua_t::lua_ref_timer(L, session, timeout, 0, false); @@ -987,7 +1152,7 @@ int32_t context_lua_t::context_query(lua_State *L) } context_lua_t::lua_free_ref_session(L, session); if (ret == NL_ETRANSTYPE) { - luaL_argerror(L, 2, common_strerror(NL_ETRANSTYPE)); + lua_error(L); return 0; } lua_pushboolean(L, 0); @@ -996,27 +1161,41 @@ int32_t context_lua_t::context_query(lua_State *L) } context_lua_t* lctx = context_lua_t::lua_get_context(L); message_t& message = lctx->get_yielding_message(); - int ret = context_check_message(L, 2, CONTEXT_QUERY, message); - if (ret != UV_OK) { - luaL_argerror(L, 2, common_strerror(ret)); + int ret = context_check_message(L, idx, count, CONTEXT_QUERY, message); + if (ret == 0) { + message_clean(message); + lua_error(L); return 0; } return lctx->lua_yield_send(L, handle, context_query_yield_finalize, NULL, context_lua_t::context_query_yield_continue, timeout); } +int32_t context_lua_t::context_query(lua_State *L) +{ + return context_query(L, false); +} + +int32_t context_lua_t::context_timed_query(lua_State *L) +{ + return context_query(L, true); +} + int32_t context_lua_t::context_reply(lua_State *L) { uint32_t handle = luaL_checkunsigned(L, 1); + if (handle == 0 || singleton_ref(node_lua_t).is_handle_illegal(handle)) { + luaL_error(L, "attempt to reply to an illegal context:0x%p", (void*)handle); + } int32_t session = luaL_checkinteger(L, 2); - int32_t ret = context_send(L, 3, handle, session, CONTEXT_REPLY); + luaL_checkany(L, 3); + int32_t ret = context_send(L, 3, lua_gettop(L) - 2, handle, session, CONTEXT_REPLY); if (ret == UV_OK) { lua_pushboolean(L, 1); return 1; } if (ret == NL_ETRANSTYPE) { - context_t* ctx = lua_get_context(L); - singleton_ref(node_lua_t).context_send(handle, ctx->get_handle(), session, CONTEXT_REPLY, NL_ETRANSTYPE); - luaL_argerror(L, 3, common_strerror(NL_ETRANSTYPE)); + singleton_ref(node_lua_t).context_send(handle, lua_get_context_handle(L), session, CONTEXT_REPLY, NL_ETRANSTYPE); + lua_error(L); return 0; } lua_pushboolean(L, 0); @@ -1037,12 +1216,26 @@ int32_t context_lua_t::context_recv_yield_finalize(lua_State *root_coro, lua_Sta return UV_OK; } -int32_t context_lua_t::context_recv_yield_continue(lua_State* L) +int32_t context_lua_t::context_recv_yield_continue(lua_State* L, int status, lua_KContext ctx) +{ + int32_t old_top = (int32_t)ctx; + bool result = lua_toboolean(L, old_top + 1); + if (!result && context_lua_t::lua_get_context(L)->get_yielding_status() != UV_OK) { + context_lua_t::lua_throw(L, old_top + 2); + } + if (result) { + lua_rotate(L, old_top + 2, 2); + } + return lua_gettop(L) - old_top; +} + +int32_t context_lua_t::context_recv_callback_adjust(lua_State* L) { - if (!lua_toboolean(L, -4) && context_lua_t::lua_get_context(L)->get_yielding_status() != UV_OK) { - context_lua_t::lua_throw(L, -3); + bool result = lua_toboolean(L, 1); //old_top is 0 + if (result) { + lua_rotate(L, 2, 2); //push source handle and session before recved data. } - return 4; + return lua_gettop(L); } int32_t context_lua_t::context_recv_timeout(lua_State *L, int32_t session, void* userdata, bool is_repeat) @@ -1056,12 +1249,15 @@ int32_t context_lua_t::context_recv(lua_State *L) { context_lua_t* lctx = (context_lua_t*)lua_get_context(L); uint32_t handle = luaL_checkunsigned(L, 1); + if (singleton_ref(node_lua_t).is_handle_illegal(handle)) { + luaL_error(L, "attempt to recv from an illegal context:0x%p", (void*)handle); + } int32_t top = lua_gettop(L); uint64_t timeout = 0; if (top >= 2) { //nonblocking if (lua_isfunction(L, 2)) { lua_settop(L, 2); - lctx->m_context_recv_sessions.make_nonblocking_callback(handle, L, 1); + lctx->m_context_recv_sessions.make_nonblocking_callback(handle, L, 0, context_recv_callback_adjust); return 0; } if (lua_isnil(L, 2)) { @@ -1077,7 +1273,7 @@ int32_t context_lua_t::context_wait_yield_finalize(lua_State *root_coro, lua_Sta { if (root_coro != NULL) { context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); - if (singleton_ref(node_lua_t).context_send(destination, lctx->get_handle(), LUA_REFNIL, LUA_CTX_WAIT, (double)destination)) { + if (singleton_ref(node_lua_t).context_send(destination, lctx->get_handle(), LUA_REFNIL, LUA_CTX_WAIT, (int64_t)destination)) { int32_t session = lctx->m_context_wait_sessions.push_blocking(destination, root_coro); int64_t timeout = lctx->get_yielding_timeout(); if (timeout > 0) { @@ -1090,7 +1286,7 @@ int32_t context_lua_t::context_wait_yield_finalize(lua_State *root_coro, lua_Sta } return UV_OK; } -int32_t context_lua_t::context_wait_yield_continue(lua_State* L) +int32_t context_lua_t::context_wait_yield_continue(lua_State* L, int status, lua_KContext ctx) { if (!lua_toboolean(L, -4)) { int32_t ret = context_lua_t::lua_get_context(L)->get_yielding_status(); @@ -1105,17 +1301,24 @@ int32_t context_lua_t::context_wait_yield_continue(lua_State* L) int32_t context_lua_t::context_wait_timeout(lua_State *L, int32_t session, void* userdata, bool is_repeat) { context_lua_t* lctx = context_lua_t::lua_get_context(L); - message_t message(0, session, LUA_CTX_WAKEUP, NL_ETIMEOUT); - return lctx->m_context_wait_sessions.wakeup_one_fixed((uint64_t)userdata, lctx, message, true); + uint32_t handle = (uint64_t)userdata; + if (!lctx->m_context_wait_sessions.is_empty(handle)) { + message_t message(0, session, LUA_CTX_WAKEUP, NL_ETIMEOUT); + int32_t result = lctx->m_context_wait_sessions.wakeup_one_fixed(handle, lctx, message, true); + if (lctx->m_context_wait_sessions.is_empty(handle)) { + singleton_ref(node_lua_t).context_send(handle, lctx->get_handle(), LUA_REFNIL, LUA_CTX_WAIT_CANCEL, UV_OK); + } + return result; + } + return 0; } int32_t context_lua_t::context_wait(lua_State *L) { context_lua_t* lctx = (context_lua_t*)lua_get_context(L); uint32_t handle = luaL_checkunsigned(L, 1); - if (handle == 0) { - luaL_error(L, "invalid context handle to wait"); - return 0; + if (handle == 0 || singleton_ref(node_lua_t).is_handle_illegal(handle)) { + luaL_error(L, "attempt to wait an illegal context:0x%p", (void*)handle); } if (handle == lctx->get_handle()) { luaL_error(L, "can't wait self to die away"); @@ -1127,7 +1330,12 @@ int32_t context_lua_t::context_wait(lua_State *L) int32_t session; if (top >= 2) { if (lua_isnil(L, 2)) { - lctx->m_context_wait_sessions.free_nonblocking_callback(handle, L); + if (!lctx->m_context_wait_sessions.is_empty(handle)) { + lctx->m_context_wait_sessions.free_nonblocking_callback(handle, L); + if (lctx->m_context_wait_sessions.is_empty(handle)) { + singleton_ref(node_lua_t).context_send(handle, lctx->get_handle(), LUA_REFNIL, LUA_CTX_WAIT_CANCEL, UV_OK); + } + } return 0; } if (lua_isfunction(L, 2)) { @@ -1142,7 +1350,7 @@ int32_t context_lua_t::context_wait(lua_State *L) } if (callback > 0) { //nonblocking lua_settop(L, callback); - if (singleton_ref(node_lua_t).context_send(handle, lctx->get_handle(), LUA_REFNIL, LUA_CTX_WAIT, (double)handle)) { + if (singleton_ref(node_lua_t).context_send(handle, lctx->get_handle(), LUA_REFNIL, LUA_CTX_WAIT, (int64_t)handle)) { lctx->m_context_wait_sessions.make_nonblocking_callback(handle, L, callback - 1, common_callback_adjust, &session); if (timeout > 0) { context_lua_t::lua_ref_timer(L, session, timeout, 0, false, (void*)handle, context_wait_timeout); @@ -1168,19 +1376,84 @@ int32_t context_lua_t::context_thread(lua_State *L) return 1; } +int32_t context_lua_t::context_log(lua_State *L) +{ + context_lua_t* lctx = context_lua_t::lua_get_context(L); + int32_t n = lua_gettop(L); /* number of arguments */ + int32_t i; + binary_t binary = { sdsnewempty(64) }; + lua_getglobal(L, "tostring"); + for (i = 1; i <= n; i++) { + const char *s; + size_t l; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tolstring(L, -1, &l); /* get result */ + if (s == NULL) { + sdsfree(binary.m_data); + return luaL_error(L, "'tostring' must return a string to 'context.log'"); + } + if (i > 1) binary.m_data = sdscatlen(binary.m_data, "\t", 1); + binary.m_data = sdscatlen(binary.m_data, s, l); + lua_pop(L, 1); /* pop result */ + } + singleton_ref(node_lua_t).log_binary_release(lctx->get_handle(), binary); + return 0; +} + +int32_t context_lua_t::context_run_callback_adjust(lua_State* L) +{ + lua_pop(L, 4); + return 0; +} + +int32_t context_lua_t::context_run(lua_State *L) +{ + int32_t top = lua_gettop(L); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (top > 1) { + lua_rotate(L, 1, top - 1); + } + int32_t session = context_lua_t::lua_ref_callback(L, top - 1, LUA_REFNIL, context_run_callback_adjust); + context_lua_t* lctx = context_lua_t::lua_get_context(L); + if (!singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), session, LUA_CTX_RUN, UV_OK)) { + context_lua_t::lua_free_ref_session(L, session); + } + return 0; +} + +int32_t context_lua_t::context_suspend_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination) +{ + if (root_coro != NULL) { + context_lua_t::lua_ref_yield_coroutine(root_coro); /* WARNINIG: this will make the coroutine dead, never go back! */ + } + return UV_OK; +} + +int32_t context_lua_t::context_suspend(lua_State *L) +{ + context_lua_t* lctx = (context_lua_t*)lua_get_context(L); + lua_settop(L, 0); + return lctx->lua_yield_send(L, lctx->get_handle(), context_suspend_yield_finalize, NULL); /* no yield continue function, never go back! */ +} + int luaopen_context(lua_State *L) { - luaL_Reg l[] = { + static const luaL_Reg l[] = { { "strerror", context_lua_t::context_strerror }, { "create", context_lua_t::context_create }, { "destroy", context_lua_t::context_destroy }, - { "self", context_lua_t::context_self }, { "thread", context_lua_t::context_thread }, { "send", context_lua_t::context_send }, { "query", context_lua_t::context_query }, + { "timed_query", context_lua_t::context_timed_query }, { "reply", context_lua_t::context_reply }, { "recv", context_lua_t::context_recv }, { "wait", context_lua_t::context_wait }, + { "log", context_lua_t::context_log }, + { "run", context_lua_t::context_run }, + { "suspend", context_lua_t::context_suspend }, { NULL, NULL }, }; luaL_newlib(L, l); @@ -1190,6 +1463,15 @@ int luaopen_context(lua_State *L) lua_pushboolean(L, 0); #endif lua_setfield(L, -2, "winos"); + context_lua_t* lctx = context_lua_t::lua_get_context(L); + lua_pushinteger(L, lctx->get_handle()); + lua_setfield(L, -2, "self"); + lua_pushstring(L, lctx->get_name()); + lua_setfield(L, -2, "name"); + lua_pushinteger(L, lctx->get_parent()); + lua_setfield(L, -2, "parent"); + lua_pushinteger(L, node_lua_t::m_cpu_count); + lua_setfield(L, -2, "ncpu"); return 1; } diff --git a/src/context_lua.h b/src/context_lua.h index f22b41c..2947651 100644 --- a/src/context_lua.h +++ b/src/context_lua.h @@ -35,6 +35,7 @@ class context_lua_t : public context_t { void on_worker_attached(); void on_worker_detached(); + const char* get_name() const { return m_name; } bool is_active() const { return m_ref_session_count > 0; } uint32_t yielding_up() { return (m_shared->m_yielding_depth) ? ++m_shared->m_yielding_depth : 0; } request_t& get_yielding_request() const { return m_shared->m_yielding_request; } @@ -48,6 +49,11 @@ class context_lua_t : public context_t { context_lua_t(const context_lua_t& lctx); context_lua_t& operator=(const context_lua_t& lctx); + /* @m_name + ** The lua context name. + */ + const char *m_name; + /* @m_lstate ** The main lvm where the lua context is running. */ @@ -93,20 +99,28 @@ class context_lua_t : public context_t { void response_context_query(message_t& response); void response_context_reply(message_t& response); void response_context_wait(message_t& response); + void response_context_wait_cancel(message_t& response); void response_context_wakeup(message_t& response); + void response_context_run(message_t& response); void response_tcp_listen(message_t& response); void response_tcp_accept(message_t& response); void response_tcp_connect(message_t& response); void response_tcp_write(message_t& response); void response_tcp_read(message_t& response); - void response_handle_close(message_t& response); void response_tcp_closing(message_t& response); + void response_udp_open(message_t& response); + void response_udp_write(message_t& response); + void response_udp_read(message_t& response); + void response_udp_closing(message_t& response); + void response_handle_close(message_t& response); void response_timeout(message_t& response); public: + static void unload(); static int32_t lua_ref_callback_entry(lua_State *L); /* for lua_ref_callback */ - static int32_t lua_ref_callback_entry_continue(lua_State *L); /* for lua_ref_callback */ + static int32_t lua_ref_callback_entry_continue(lua_State *L, int status, lua_KContext ctx); /* for lua_ref_callback */ static int32_t lua_ref_callback_entry_finish(lua_State *L, int32_t status); + static int32_t lua_ref_callback_error_handler(lua_State *L); /* for lua_ref_callback */ public: static uint32_t lua_get_context_handle(lua_State *L); @@ -115,8 +129,8 @@ class context_lua_t : public context_t { static void lua_throw(lua_State *L, int32_t idx); /******** the following are yielding facilities ********/ - static int32_t common_yield_continue(lua_State* L); - static int32_t lua_yield_send(lua_State *L, uint32_t destination, yiled_finalize_t fnz, void* fnz_ud = NULL, lua_CFunction yieldk = NULL, int64_t timeout = 0); + static int32_t common_yield_continue(lua_State* L, int status, lua_KContext ctx); + static int32_t lua_yield_send(lua_State *L, uint32_t destination, yiled_finalize_t fnz, void* fnz_ud = NULL, lua_KFunction yieldk = NULL, int64_t timeout = 0); static int32_t common_callback_adjust(lua_State* L); static int32_t lua_ref_session(lua_State *L, int32_t idx); static void lua_unref_session(lua_State *L, int32_t idx, int32_t session); @@ -133,15 +147,20 @@ class context_lua_t : public context_t { /********** the following are context lua api **********/ static int32_t context_check_message(lua_State *L, int32_t idx, uint32_t msg_type, message_t& message); - static int32_t context_send(lua_State *L, int32_t idx, uint32_t handle, int32_t session, uint32_t msg_type); + static int32_t context_check_message(lua_State *L, int32_t idx, uint32_t count, uint32_t msg_type, message_t& message); + static int32_t context_send(lua_State *L, int32_t idx, uint32_t count, uint32_t handle, int32_t session, uint32_t msg_type); + static int32_t context_query(lua_State *L, bool timed_query); static int32_t context_query_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); - static int32_t context_query_yield_continue(lua_State* L); + static int32_t context_query_yield_continue(lua_State* L, int status, lua_KContext ctx); static int32_t context_recv_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); - static int32_t context_recv_yield_continue(lua_State* L); + static int32_t context_recv_yield_continue(lua_State* L, int status, lua_KContext ctx); + static int32_t context_recv_callback_adjust(lua_State* L); static int32_t context_recv_timeout(lua_State *L, int32_t session, void* userdata, bool is_repeat); static int32_t context_wait_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); - static int32_t context_wait_yield_continue(lua_State* L); + static int32_t context_wait_yield_continue(lua_State* L, int status, lua_KContext ctx); static int32_t context_wait_timeout(lua_State *L, int32_t session, void* userdata, bool is_repeat); + static int32_t context_run_callback_adjust(lua_State* L); + static int32_t context_suspend_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); static int32_t context_strerror(lua_State *L); static int32_t context_self(lua_State *L); static int32_t context_thread(lua_State *L); @@ -149,9 +168,13 @@ class context_lua_t : public context_t { static int32_t context_destroy(lua_State *L); static int32_t context_send(lua_State *L); static int32_t context_query(lua_State *L); + static int32_t context_timed_query(lua_State *L); static int32_t context_reply(lua_State *L); static int32_t context_recv(lua_State *L); static int32_t context_wait(lua_State *L); + static int32_t context_log(lua_State *L); + static int32_t context_run(lua_State *L); + static int32_t context_suspend(lua_State *L); /************ the above are context lua api ************/ public: diff --git a/src/context_mgr.cpp b/src/context_mgr.cpp index eb65feb..d030ffe 100644 --- a/src/context_mgr.cpp +++ b/src/context_mgr.cpp @@ -20,6 +20,13 @@ context_mgr_t::~context_mgr_t() } } +void context_mgr_t::set_handle_index(uint32_t index) +{ + uv_rwlock_wrlock(&m_lock); + m_handle_index = index; + uv_rwlock_wrunlock(&m_lock); +} + void context_mgr_t::expand_slot() { uint32_t new_cap = m_slot_cap << 2; @@ -35,7 +42,7 @@ void context_mgr_t::expand_slot() m_slot_cap = new_cap; } -bool context_mgr_t::register_context( context_t* ctx ) +bool context_mgr_t::register_context(context_t* ctx, uint32_t parent) { uv_rwlock_wrlock(&m_lock); if (m_ctx_size == MAX_CTX_SIZE || ctx->get_handle() != 0) { @@ -54,7 +61,7 @@ bool context_mgr_t::register_context( context_t* ctx ) m_ctx_slot[hash] = ctx; m_handle_index = handle + 1; ++m_ctx_size; - ctx->on_registered(handle); + ctx->on_registered(parent, handle); uv_rwlock_wrunlock(&m_lock); return true; } diff --git a/src/context_mgr.h b/src/context_mgr.h index 5cb3296..3bf603d 100644 --- a/src/context_mgr.h +++ b/src/context_mgr.h @@ -7,9 +7,10 @@ class context_mgr_t { public: context_mgr_t(); ~context_mgr_t(); - bool register_context(context_t* ctx); + bool register_context(context_t* ctx, uint32_t parent); void retire_context(context_t* ctx); context_t* grab_context(uint32_t handle); + void set_handle_index(uint32_t index); uint32_t get_context_count() const { return m_ctx_size; } private: void expand_slot(); diff --git a/src/endian_conv.cpp b/src/endian_conv.cpp index dddf548..3a2e51e 100644 --- a/src/endian_conv.cpp +++ b/src/endian_conv.cpp @@ -1,6 +1,16 @@ #include "common.h" #include "endian_conv.h" +static union { char c[4]; unsigned long mylong; } endian_test = { { LITTLE_ENDIAN_VAL, '?', '?', BIG_ENDIAN_VAL } }; +#define ENV_ENDIAN_VAL ((char)endian_test.mylong) +#define ENV_IS_LITTEL_ENDIAN (ENV_ENDIAN_VAL == LITTLE_ENDIAN_VAL) +#define ENV_IS_BIG_ENDIAN (ENV_ENDIAN_VAL == BIG_ENDIAN_VAL) + +char env_endian() +{ + return ENV_ENDIAN_VAL; +} + int16_t endian_to_int16(char *src, char src_endian) { if (src_endian == ENV_ENDIAN_VAL) { diff --git a/src/endian_conv.h b/src/endian_conv.h index 350bd54..b8c59fc 100644 --- a/src/endian_conv.h +++ b/src/endian_conv.h @@ -3,11 +3,8 @@ #define LITTLE_ENDIAN_VAL ((char)'L') #define BIG_ENDIAN_VAL ((char)'B') -static union { char c[4]; unsigned long mylong; } endian_test = { { LITTLE_ENDIAN_VAL, '?', '?', BIG_ENDIAN_VAL } }; -#define ENV_ENDIAN_VAL ((char)endian_test.mylong) -#define ENV_IS_LITTEL_ENDIAN (ENV_ENDIAN_VAL == LITTLE_ENDIAN_VAL) -#define ENV_IS_BIG_ENDIAN (ENV_ENDIAN_VAL == BIG_ENDIAN_VAL) +extern char env_endian(); extern int16_t endian_to_int16(char *src, char src_endian); extern int32_t endian_to_int32(char *src, char src_endian); extern int64_t endian_to_int64(char *src, char src_endian); diff --git a/src/errors.h b/src/errors.h index 342d0a2..ea6e03f 100644 --- a/src/errors.h +++ b/src/errors.h @@ -5,15 +5,21 @@ /* Expand this list if necessary. */ #define NL_ERRNO_MAP(XX) \ - XX( 200, EYIELD, "attempt to yield across a C-call boundary") \ + XX( 200, EYIELD, "attempt to yield across a C-call boundary") \ XX( 201, EYIELDFNZ, "yielding up finalize error") \ XX( 202, ESTACKLESS, "attempt to yield across a stack-less coroutine") \ XX( 203, ETCPSCLOSED, "tcp socket has been closed") \ XX( 204, ETCPLCLOSED, "tcp listen socket has been closed") \ - XX( 205, ENOCONTEXT, "context not exist or has been killed") \ - XX( 206, ETRANSTYPE, "transfer data type not supported") \ - XX( 207, ENOREPLY, "no reply") \ - XX( 208, ETIMEOUT, "timeout") \ + XX( 205, ETCPNOWSHARED, "tcp socket not set write shared") \ + XX( 206, ETCPWRITELONG, "attempt to send data too long") \ + XX( 207, ETCPREADOVERFLOW,"read overflowed wake up") \ + XX( 208, EUDPSCLOSED, "udp socket has been closed") \ + XX( 209, EUDPNOWSHARED, "udp socket not set write shared") \ + XX( 210, ENOCONTEXT, "context not exist or has been killed") \ + XX( 211, ETRANSTYPE, "transfer data type not supported") \ + XX( 212, ENOREPLY, "no reply") \ + XX( 213, ETIMEOUT, "timeout") \ + XX( 214, ESOCKFAIL, "socket create failed") \ #define NL_ERRNO_GEN(val, name, s) NL_##name = val, typedef enum { diff --git a/src/handle_base_def.h b/src/handle_base_def.h index fd266a6..0dc91a6 100644 --- a/src/handle_base_def.h +++ b/src/handle_base_def.h @@ -8,4 +8,7 @@ typedef enum TIMER_SET = 2, } handle_set; +#define SOCKET_MAKE_FD(lua_ref, context_id) ((int64_t)(lua_ref)%1000000 + 1000000*(int64_t)(context_id)) +#define SOCKET_FD_SOURCE(fd) ((int64_t)(fd)/1000000) + #endif \ No newline at end of file diff --git a/src/lbinary.h b/src/lbinary.h new file mode 100644 index 0000000..2e94981 --- /dev/null +++ b/src/lbinary.h @@ -0,0 +1,9 @@ +#ifndef LBINARY_H_ +#define LBINARY_H_ +#include "sds.h" + +typedef struct binary_t { + char* m_data; +} binary_t; + +#endif \ No newline at end of file diff --git a/src/lbuffer.cpp b/src/lbuffer.cpp index 4b47090..1211aca 100644 --- a/src/lbuffer.cpp +++ b/src/lbuffer.cpp @@ -14,7 +14,7 @@ static int32_t lbuffer_new(lua_State* L) } else if (type == LUA_TSTRING) { init = lua_tolstring(L, 1, &init_len); cap = init_len + DEFAULT_BUFFER_CAPACITY; - } else if (type != LUA_TNIL) { + } else if (type != LUA_TNIL && type != LUA_TNONE) { buffer_t* buffer = (buffer_t*)luaL_testudata(L, 1, BUFFER_METATABLE); if (!buffer) { luaL_argerror(L, 1, "unexpected argument type"); @@ -53,6 +53,27 @@ static int32_t lbuffer_split(lua_State* L) return 1; } +/* translate a relative string position: negative means back from end */ +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 int32_t lbuffer_remove(lua_State* L) +{ + buffer_t* buffer = (buffer_t*)luaL_checkudata(L, 1, BUFFER_METATABLE); + lua_Integer length = buffer_data_length(*buffer); + lua_Integer pos = posrelat(luaL_optinteger(L, 2, 1), length); + + size_t start = pos >= 1 ? pos - 1 : 0; + size_t rmlen = luaL_optunsigned(L, 3, length); + + rmlen = buffer_remove(*buffer, start, rmlen); + lua_pushinteger(L, rmlen); + return 1; +} + static int32_t lbuffer_clear(lua_State* L) { buffer_t* buffer = (buffer_t*)luaL_checkudata(L, 1, BUFFER_METATABLE); @@ -91,9 +112,10 @@ static int32_t lbuffer_find(lua_State* L) if (buffer_is_valid(*buffer)) { int32_t top = lua_gettop(L); if (top >= 2) { - const char* find = strstr(buffer_data_ptr(*buffer), luaL_checklstring(L, 2, NULL)); + const char* ptr = buffer_data_ptr(*buffer); + const char* find = strstr(ptr, luaL_checklstring(L, 2, NULL)); if (find) { - lua_pushinteger(L, (int32_t)(find - buffer_data_ptr(*buffer)) + 1); + lua_pushinteger(L, (int32_t)(find - ptr) + 1); return 1; } return 0; @@ -122,12 +144,477 @@ static int32_t lbuffer_release(lua_State* L) return 0; } + +/* +** Some sizes are better limited to fit in 'int', but must also fit in +** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) +*/ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +#define MAXSIZE \ + (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) + +/* +** {====================================================== +** PACK/UNPACK +** ======================================================= +*/ + + +/* value used for padding */ +#if !defined(LUA_PACKPADBYTE) +#define LUA_PACKPADBYTE 0x00 +#endif + +/* maximum size for the binary representation of an integer */ +#define MAXINTSIZE 16 + +/* number of bits in a character */ +#define NB CHAR_BIT + +/* mask for one character (NB 1's) */ +#define MC ((1 << NB) - 1) + +/* size of a lua_Integer */ +#define SZINT ((int)sizeof(lua_Integer)) + + +/* dummy union to get native endianness */ +static const union { + int dummy; + char little; /* true iff machine is little endian */ +} nativeendian = { 1 }; + + +/* dummy structure to get native alignment requirements */ +struct cD { + char c; + union { double d; void *p; lua_Integer i; lua_Number n; } u; +}; + +#define MAXALIGN (offsetof(struct cD, u)) + + +/* +** Union for serializing floats +*/ +typedef union Ftypes { + float f; + double d; + lua_Number n; + char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ +} Ftypes; + + +/* +** information to pack/unpack stuff +*/ +typedef struct Header { + lua_State *L; + int islittle; + int maxalign; +} Header; + + +/* +** options for pack/unpack +*/ +typedef enum KOption { + Kint, /* signed integers */ + Kuint, /* unsigned integers */ + Kfloat, /* floating-point numbers */ + Kchar, /* fixed-length strings */ + Kstring, /* strings with prefixed length */ + Kzstr, /* zero-terminated strings */ + Kpadding, /* padding */ + Kpaddalign, /* padding for alignment */ + Knop /* no-op (configuration or spaces) */ +} KOption; + + +/* +** Read an integer numeral from string 'fmt' or return 'df' if +** there is no numeral +*/ +static int digit(int c) { return '0' <= c && c <= '9'; } + +static int getnum(const char **fmt, int df) { + if (!digit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + int a = 0; + do { + a = a * 10 + (*((*fmt)++) - '0'); + } while (digit(**fmt) && a <= ((int)MAXSIZE - 9) / 10); + return a; + } +} + + +/* +** Read an integer numeral and raises an error if it is larger +** than the maximum size for integers. +*/ +static int getnumlimit(Header *h, const char **fmt, int df) { + int sz = getnum(fmt, df); + if (sz > MAXINTSIZE || sz <= 0) + luaL_error(h->L, "integral size (%d) out of limits [1,%d]", + sz, MAXINTSIZE); + return sz; +} + + +/* +** Initialize Header +*/ +static void initheader(lua_State *L, Header *h) { + h->L = L; + h->islittle = nativeendian.little; + h->maxalign = 1; +} + + +/* +** Read and classify next option. 'size' is filled with option's size. +*/ +static KOption getoption(Header *h, const char **fmt, int *size) { + int opt = *((*fmt)++); + *size = 0; /* default */ + switch (opt) { + case 'b': *size = sizeof(char); return Kint; + case 'B': *size = sizeof(char); return Kuint; + case 'h': *size = sizeof(short); return Kint; + case 'H': *size = sizeof(short); return Kuint; + case 'l': *size = sizeof(long); return Kint; + case 'L': *size = sizeof(long); return Kuint; + case 'j': *size = sizeof(lua_Integer); return Kint; + case 'J': *size = sizeof(lua_Integer); return Kuint; + case 'T': *size = sizeof(size_t); return Kuint; + case 'f': *size = sizeof(float); return Kfloat; + case 'd': *size = sizeof(double); return Kfloat; + case 'n': *size = sizeof(lua_Number); return Kfloat; + case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; + case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; + case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; + case 'c': + *size = getnum(fmt, -1); + if (*size == -1) + luaL_error(h->L, "missing size for format option 'c'"); + return Kchar; + case 'z': return Kzstr; + case 'x': *size = 1; return Kpadding; + case 'X': return Kpaddalign; + case ' ': break; + case '<': h->islittle = 1; break; + case '>': h->islittle = 0; break; + case '=': h->islittle = nativeendian.little; break; + case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; + default: luaL_error(h->L, "invalid format option '%c'", opt); + } + return Knop; +} + + +/* +** Read, classify, and fill other details about the next option. +** 'psize' is filled with option's size, 'notoalign' with its +** alignment requirements. +** Local variable 'size' gets the size to be aligned. (Kpadal option +** always gets its full alignment, other options are limited by +** the maximum alignment ('maxalign'). Kchar option needs no alignment +** despite its size. +*/ +static KOption getdetails(Header *h, size_t totalsize, + const char **fmt, int *psize, int *ntoalign) { + KOption opt = getoption(h, fmt, psize); + int align = *psize; /* usually, alignment follows size */ + if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ + if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) + luaL_argerror(h->L, 1, "invalid next option for option 'X'"); + } + if (align <= 1 || opt == Kchar) /* need no alignment? */ + *ntoalign = 0; + else { + if (align > h->maxalign) /* enforce maximum alignment */ + align = h->maxalign; + if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ + luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); + *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); + } + return opt; +} + + +/* +** Pack integer 'n' with 'size' bytes and 'islittle' endianness. +** The final 'if' handles the case when 'size' is larger than +** the size of a Lua integer, correcting the extra sign-extension +** bytes if necessary (by default they would be zeros). +*/ +static void packint(lua_State *L, buffer_t *b, lua_Unsigned n, + int islittle, int size, int neg) { + char *buff = buffer_reserve(*b, size); + if (!buff) + luaL_error(L, "not enough memory for lbuffer allocation"); + + int i; + buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ + for (i = 1; i < size; i++) { + n >>= NB; + buff[islittle ? i : size - 1 - i] = (char)(n & MC); + } + if (neg && size > SZINT) { /* negative number need sign extension? */ + for (i = SZINT; i < size; i++) /* correct extra bytes */ + buff[islittle ? i : size - 1 - i] = (char)MC; + } + buffer_adjust_len(*b, size); /* add result to buffer */ +} + + +/* +** Copy 'size' bytes from 'src' to 'dest', correcting endianness if +** given 'islittle' is different from native endianness. +*/ +static void copywithendian(volatile char *dest, volatile const char *src, + int size, int islittle) { + if (islittle == nativeendian.little) { + while (size-- != 0) + *(dest++) = *(src++); + } + else { + dest += size - 1; + while (size-- != 0) + *(dest--) = *(src++); + } +} + + +static int lbuffer_pack(lua_State *L) { + Header h; + buffer_t* b = (buffer_t*)luaL_checkudata(L, 1, BUFFER_METATABLE); + if (!buffer_is_valid(*b)) + luaL_error(L, "invalid lbuffer to pack"); + const char *fmt = luaL_checkstring(L, 2); /* format string */ + int arg = 2; /* current argument to pack */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + totalsize += ntoalign + size; + while (ntoalign-- > 0) + buffer_append_char(*b, LUA_PACKPADBYTE); /* fill alignment */ + arg++; + switch (opt) { + case Kint: { /* signed integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) { /* need overflow check? */ + lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); + luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); + } + packint(L, b, (lua_Unsigned)n, h.islittle, size, (n < 0)); + break; + } + case Kuint: { /* unsigned integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) /* need overflow check? */ + luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), + arg, "unsigned overflow"); + packint(L, b, (lua_Unsigned)n, h.islittle, size, 0); + break; + } + case Kfloat: { /* floating-point options */ + volatile Ftypes u; + char *buff = buffer_reserve(*b, size); + lua_Number n = luaL_checknumber(L, arg); /* get argument */ + if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ + else if (size == sizeof(u.d)) u.d = (double)n; + else u.n = n; + /* move 'u' to final result, correcting endianness if needed */ + copywithendian(buff, u.buff, size, h.islittle); + buffer_adjust_len(*b, size); /* add result to buffer */ + break; + } + case Kchar: { /* fixed-size string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + if ((size_t)size <= len) /* string larger than (or equal to) needed? */ + buffer_append(*b, s, size); /* truncate string to asked size */ + else { /* string smaller than needed */ + buffer_append(*b, s, len); /* add it all */ + while (len++ < (size_t)size) /* pad extra space */ + buffer_append_char(*b, LUA_PACKPADBYTE); + } + break; + } + case Kstring: { /* strings with length count */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, size >= (int)sizeof(size_t) || + len < ((size_t)1 << (size * NB)), + arg, "string length does not fit in given size"); + packint(L, b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ + buffer_append(*b, s, len); + totalsize += len; + break; + } + case Kzstr: { /* zero-terminated string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); + buffer_append(*b, s, len); + buffer_append_char(*b, '\0'); /* add zero at the end */ + totalsize += len + 1; + break; + } + case Kpadding: buffer_append_char(*b, LUA_PACKPADBYTE); /* FALLTHROUGH */ + case Kpaddalign: case Knop: + arg--; /* undo increment */ + break; + } + } + return 0; +} + + +static int lbuffer_packsize(lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + size += ntoalign; /* total space used by option */ + luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, + "format result too large"); + totalsize += size; + switch (opt) { + case Kstring: /* strings with length count */ + case Kzstr: /* zero-terminated string */ + luaL_argerror(L, 1, "variable-length format"); + /* call never return, but to avoid warnings: *//* FALLTHROUGH */ + default: break; + } + } + lua_pushinteger(L, (lua_Integer)totalsize); + return 1; +} + + +/* +** Unpack an integer with 'size' bytes and 'islittle' endianness. +** If size is smaller than the size of a Lua integer and integer +** is signed, must do sign extension (propagating the sign to the +** higher bits); if size is larger than the size of a Lua integer, +** it must check the unread bytes to see whether they do not cause an +** overflow. +*/ +static lua_Integer unpackint(lua_State *L, const char *str, + int islittle, int size, int issigned) { + lua_Unsigned res = 0; + int i; + int limit = (size <= SZINT) ? size : SZINT; + for (i = limit - 1; i >= 0; i--) { + res <<= NB; + res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + } + if (size < SZINT) { /* real size smaller than lua_Integer? */ + if (issigned) { /* needs sign extension? */ + lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); + res = ((res ^ mask) - mask); /* do sign extension */ + } + } + else if (size > SZINT) { /* must check unread bytes */ + int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; + for (i = limit; i < size; i++) { + if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) + luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); + } + } + return (lua_Integer)res; +} + + +static int lbuffer_unpack(lua_State *L) { + Header h; + buffer_t* b = (buffer_t*)luaL_checkudata(L, 1, BUFFER_METATABLE); + if (!buffer_is_valid(*b)) + luaL_error(L, "invalid lbuffer to unpack"); + const char *fmt = luaL_checkstring(L, 2); /* format string */ + size_t ld = buffer_data_length(*b); + const char *data = buffer_data_ptr(*b); + size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1; + int n = 0; /* number of results */ + luaL_argcheck(L, pos <= ld, 3, "initial position out of lbuffer"); + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); + if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld) + luaL_argerror(L, 2, "data string too short"); + pos += ntoalign; /* skip alignment */ + /* stack space for item + next position */ + luaL_checkstack(L, 2, "too many results"); + n++; + switch (opt) { + case Kint: + case Kuint: { + lua_Integer res = unpackint(L, data + pos, h.islittle, size, + (opt == Kint)); + lua_pushinteger(L, res); + break; + } + case Kfloat: { + volatile Ftypes u; + lua_Number num; + copywithendian(u.buff, data + pos, size, h.islittle); + if (size == sizeof(u.f)) num = (lua_Number)u.f; + else if (size == sizeof(u.d)) num = (lua_Number)u.d; + else num = u.n; + lua_pushnumber(L, num); + break; + } + case Kchar: { + lua_pushlstring(L, data + pos, size); + break; + } + case Kstring: { + size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); + luaL_argcheck(L, pos + len + size <= ld, 2, "data string too short"); + lua_pushlstring(L, data + pos + size, len); + pos += len; /* skip string */ + break; + } + case Kzstr: { + size_t len = (int)strlen(data + pos); + lua_pushlstring(L, data + pos, len); + pos += len + 1; /* skip string plus final '\0' */ + break; + } + case Kpaddalign: case Kpadding: case Knop: + n--; /* undo increment */ + break; + } + pos += size; + } + lua_pushinteger(L, pos + 1); /* next position */ + return n + 1; +} + +/* }====================================================== */ + ///////////////////////////////////////////////////////////////////////////////////////////////////////// -static luaL_Reg lbuffer[] = { +static const luaL_Reg lbuffer[] = { { "new", lbuffer_new }, { "append", lbuffer_append }, + { "pack", lbuffer_pack }, + { "unpack", lbuffer_unpack }, + { "packsize", lbuffer_packsize }, { "split", lbuffer_split }, + { "remove", lbuffer_remove }, { "clear", lbuffer_clear }, { "length", lbuffer_length }, { "unfill", lbuffer_unfill }, @@ -136,6 +623,7 @@ static luaL_Reg lbuffer[] = { { "valid", lbuffer_valid }, { "release", lbuffer_release }, { "__gc", lbuffer_release }, + { "__len", lbuffer_length }, { "__tostring", lbuffer_tostring }, { NULL, NULL }, }; diff --git a/src/lbuffer.h b/src/lbuffer.h index 61e43e3..cbf10fc 100644 --- a/src/lbuffer.h +++ b/src/lbuffer.h @@ -2,7 +2,7 @@ #define LBUFFER_H_ #include "buffer.h" -#define BUFFER_METATABLE "class.buffer_t" +#define BUFFER_METATABLE "class buffer_t" extern buffer_t* create_buffer(lua_State* L); extern buffer_t* create_buffer(lua_State* L, size_t cap, const char *init, size_t init_len); diff --git a/src/lcororep.cpp b/src/lcororep.cpp index 2ce88d2..bfa1b2a 100644 --- a/src/lcororep.cpp +++ b/src/lcororep.cpp @@ -1,7 +1,7 @@ #include "common.h" #include "context_lua.h" -static int lua_coresume (lua_State *L); +static int32_t coresume_continue(lua_State *L, int status, lua_KContext ctx); static int auxresume (lua_State *L, lua_State *co, int narg) { int status, stacks; @@ -22,8 +22,8 @@ static int auxresume (lua_State *L, lua_State *co, int narg) { lua_xmove(L, co, narg); context_lua_t *lctx = context_lua_t::lua_get_context(L); while (((status = lua_resume(co, L, narg)) == LUA_YIELD) && lctx->yielding_up()) { /* can happen only when status == LUA_YIELD */ - if ((stacks = lua_checkstack(L, LUA_MINSTACK)) && lua_yieldable(L)) { /* reserve LUA_MINSTACK stack for resume result */ - return lua_yieldk(L, 0, 0, lua_coresume); /* yield again! n(0) result to yield and ctx(0) is not allowed */ + if ((stacks = lua_checkstack(L, LUA_MINSTACK)) && lua_isyieldable(L)) { /* reserve LUA_MINSTACK stack for resume result */ + return lua_yieldk(L, 0, 0, coresume_continue); /* yield again! n(0) result to yield and ctx(0) is not allowed */ } else { /* lua_gettop(co) must be 0 since context_lua_t::lua_yield_send yield 0 result */ int yieldup_status = stacks ? NL_EYIELD : NL_ESTACKLESS; lctx->yielding_finalize(NULL, yieldup_status); @@ -49,7 +49,6 @@ static int auxresume (lua_State *L, lua_State *co, int narg) { } } - static int lua_coresume (lua_State *L) { lua_State *co = lua_tothread(L, 1); int r; @@ -67,6 +66,10 @@ static int lua_coresume (lua_State *L) { } } +static int32_t coresume_continue(lua_State *L, int status, lua_KContext ctx) +{ + return lua_coresume(L); +} static int lua_auxwrap (lua_State *L) { lua_State *co = lua_tothread(L, lua_upvalueindex(1)); diff --git a/src/lshared.cpp b/src/lshared.cpp new file mode 100644 index 0000000..8fba1e3 --- /dev/null +++ b/src/lshared.cpp @@ -0,0 +1,52 @@ +#include "lshared.h" + +int shared_t::meta_index(lua_State *L) +{ + shared_t** shared = (shared_t**)luaL_checkudata(L, 1, SHARED_METATABLE); + if (*shared) { + const char* name = lua_tostring(L, 2); + if (name) { + lua_CFunction func = (*shared)->index_function(name); + if (func) { + lua_pushcfunction(L, func); + return 1; + } + } + } + return 0; +} + +int shared_t::meta_gc(lua_State *L) +{ + shared_t** shared = (shared_t**)luaL_checkudata(L, 1, SHARED_METATABLE); + if (*shared) { + (*shared)->release(); + *shared = NULL; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +shared_t** shared_t::create(lua_State* L) +{ + shared_t** ret = (shared_t**)lua_newuserdata(L, sizeof(shared_t*)); + *ret = NULL; + if (luaL_newmetatable(L, SHARED_METATABLE)) { /* create new metatable */ + lua_pushcclosure(L, meta_index, 0); + lua_setfield(L, -2, "__index"); + lua_pushcclosure(L, meta_gc, 0); + lua_setfield(L, -2, "__gc"); + } + lua_setmetatable(L, -2); + return ret; +} + +shared_t** shared_t::create(lua_State* L, shared_t* shared) +{ + shared_t** ret = create(L); + shared->grab(); + *ret = shared; + return ret; +} + diff --git a/src/lshared.h b/src/lshared.h new file mode 100644 index 0000000..41f79b1 --- /dev/null +++ b/src/lshared.h @@ -0,0 +1,59 @@ +#ifndef LSHARED_H_ +#define LSHARED_H_ +#include "common.h" +#include + +#define SHARED_METATABLE "class shared_t" + +class shared_t { +public: + shared_t() : m_ref(1) {} + + FORCE_INLINE shared_t* grab() { atomic_inc(m_ref); return this; } + FORCE_INLINE void release() { if (atomic_dec(m_ref) == 1) delete this; } + FORCE_INLINE bool writeable() { return m_ref == 1; } + +protected: + shared_t(const shared_t& shared); + shared_t& operator=(const shared_t& shared); + + virtual ~shared_t() {}; + +private: + static int meta_index(lua_State *L); + static int meta_gc(lua_State *L); + + /* + * index_function must return a multi-thread safe lua_CFunction on shared_t object. + * It's better all operations on the shared_t object is read-only or thread safe. + * index_function is not essential for the derived class unless you want a meta-method for your userdata, + * so you can choose whether overwriting this function in the derived class. + **/ + virtual lua_CFunction index_function(const char* name) { return NULL; } + +public: + static shared_t** create(lua_State* L); + static shared_t** create(lua_State* L, shared_t* shared); + + template < class type > + static type* check_shared(lua_State *L, int idx, bool writable) + { + shared_t* shared = *(shared_t**)luaL_checkudata(L, idx, SHARED_METATABLE); + if (shared == NULL) { + luaL_argerror(L, 1, "shared_t invalid"); + } + if (writable && !shared->writeable()) { + luaL_argerror(L, 1, "shared_t not writable"); + } + type* p = dynamic_cast(shared); + if (!p) { + luaL_error(L, "bad argument #%d (%s expected, %s found)", idx, typeid(type).name(), typeid(*shared).name()); + } + return p; + } + +private: + atomic_t m_ref; +}; + +#endif diff --git a/src/ltpack.cpp b/src/ltpack.cpp new file mode 100644 index 0000000..13a70dd --- /dev/null +++ b/src/ltpack.cpp @@ -0,0 +1,198 @@ +#define __STDC_LIMIT_MACROS +#include "ltpack.h" +#ifndef CC_MSVC +#include +#endif + +#define MAX_DEPTH 256 + +#define TYPE_INT8 0 +#define TYPE_INT16 1 +#define TYPE_INT32 2 +#define TYPE_INT64 3 +#define TYPE_DOUBLE 4 +#define TYPE_STRING 5 +#define TYPE_BOOL 6 +#define TYPE_TABLE_BEG 7 +#define TYPE_TABLE_END 8 + +static inline void +pack_integer(tpack_t *pack, int64_t v) { + if (v >= INT8_MIN && v <= INT8_MAX) { + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT8); + pack->m_data = sdscat(pack->m_data, (int8_t)v); + return; + } + if (v >= INT16_MIN && v <= INT16_MAX) { + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT16); + pack->m_data = sdscat(pack->m_data, (int16_t)v); + return; + } + if (v >= INT32_MIN && v <= INT32_MAX) { + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT32); + pack->m_data = sdscat(pack->m_data, (int32_t)v); + return; + } + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT64); + pack->m_data = sdscat(pack->m_data, (int64_t)v); +} + +static inline void +pack_double(tpack_t *pack, double v) { + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_DOUBLE); + pack->m_data = sdscat(pack->m_data, v); +} + +static inline void +pack_boolean(tpack_t *pack, bool v) { + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_BOOL); + pack->m_data = sdscat(pack->m_data, v); +} + +static inline void +pack_string(tpack_t *pack, const char* v, size_t len) { + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_STRING); + pack->m_data = sdscat(pack->m_data, len); + pack->m_data = sdscatlen(pack->m_data, v, len); +} + +static void +pack_lua_data(tpack_t *pack, lua_State *L, int idx, int depth); + +static void +pack_table(tpack_t *pack, lua_State *L, int idx, int depth) { + if (depth > MAX_DEPTH) { + luaL_error(L, "too deep(%d, limit %d) while packing table", depth, MAX_DEPTH); + } + idx = lua_absindex(L, idx); + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_TABLE_BEG); + luaL_checkstack(L, 2, NULL); + lua_pushnil(L); + while (lua_next(L, idx) != 0) { + pack_lua_data(pack, L, -2, depth); + pack_lua_data(pack, L, -1, depth); + lua_pop(L, 1); + } + pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_TABLE_END); +} + +static void +pack_lua_data(tpack_t *pack, lua_State *L, int idx, int depth) { + int data_type = lua_type(L, idx); + const char * str = NULL; + size_t len; + switch (data_type) { + case LUA_TNUMBER: + if (lua_isinteger(L, idx)) { + pack_integer(pack, lua_tointeger(L, idx)); + } else { + pack_double(pack, lua_tonumber(L, idx)); + } + return; + case LUA_TSTRING: + str = lua_tolstring(L, idx, &len); + pack_string(pack, str, len); + return; + case LUA_TBOOLEAN: + pack_boolean(pack, (bool)lua_toboolean(L, idx)); + return; + case LUA_TTABLE: + pack_table(pack, L, idx, depth + 1); + return; + default: + luaL_error(L, "attempt to pack a %s value", lua_typename(L, data_type)); + return; + } +} + +static int8_t +unpack_lua_data(tpack_t *pack, size_t& rpos, lua_State *L) { + luaL_checkstack(L, 1, NULL); + int8_t data_type = sdsread(pack->m_data, rpos); + size_t len; + ++rpos; + switch (data_type) { + case TYPE_INT8: + lua_pushinteger(L, sdsread(pack->m_data, rpos)); + rpos += 1; + break; + case TYPE_INT16: + lua_pushinteger(L, sdsread(pack->m_data, rpos)); + rpos += 2; + break; + case TYPE_INT32: + lua_pushinteger(L, sdsread(pack->m_data, rpos)); + rpos += 4; + break; + case TYPE_INT64: + lua_pushinteger(L, sdsread(pack->m_data, rpos)); + rpos += 8; + break; + case TYPE_DOUBLE: + lua_pushnumber(L, sdsread(pack->m_data, rpos)); + rpos += sizeof(double); + break; + case TYPE_STRING: + len = sdsread(pack->m_data, rpos); + rpos += sizeof(size_t); + lua_pushlstring(L, sdsread(pack->m_data, rpos, len), len); + rpos += len; + break; + case TYPE_BOOL: + lua_pushboolean(L, sdsread(pack->m_data, rpos)); + rpos += sizeof(bool); + break; + case TYPE_TABLE_BEG: + lua_createtable(L, 0, 0); + while (unpack_lua_data(pack, rpos, L) != TYPE_TABLE_END) { + unpack_lua_data(pack, rpos, L); + lua_rawset(L, -3); + } + break; + case TYPE_TABLE_END: + break; + default: + break; + } + return data_type; +} + +///////////////////////////////////////////////////////////////////////// + +static int pack_safe(lua_State *L) { + tpack_t* pack = (tpack_t*)lua_touserdata(L, 2); //arg1: table, arg2: tpack_t ptr. + lua_settop(L, 1); + pack_lua_data(pack, L, 1, 0); + return 0; +} + +bool ltpack(tpack_t* pack, lua_State *L, int idx) { + if (lua_istable(L, idx)) { + lua_checkstack(L, 3); + lua_pushcfunction(L, pack_safe); + lua_pushvalue(L, idx); + lua_pushlightuserdata(L, pack); + if (lua_pcall(L, 2, LUA_MULTRET, NULL) == LUA_OK) { + return true; + } + return false; + } + lua_checkstack(L, 1); + lua_pushstring(L, "table expected"); + return false; +} + +static int unpack_safe(lua_State *L) { + size_t rpos = 0; + tpack_t* pack = (tpack_t*)lua_touserdata(L, 1); //arg1: tpack_t ptr. + lua_settop(L, 1); + unpack_lua_data(pack, rpos, L); + return 1; +} + +extern bool ltunpack(tpack_t* pack, lua_State *L) { + lua_checkstack(L, 3); + lua_pushcfunction(L, unpack_safe); + lua_pushlightuserdata(L, pack); + return lua_pcall(L, 1, LUA_MULTRET, NULL) == LUA_OK; +} diff --git a/src/ltpack.h b/src/ltpack.h new file mode 100644 index 0000000..d4bf7ca --- /dev/null +++ b/src/ltpack.h @@ -0,0 +1,13 @@ +#ifndef LTPACK_H_ +#define LTPACK_H_ +#include "common.h" +#include "sds.h" + +typedef struct tpack_t { + sds m_data; +} tpack_t; + +extern bool ltpack(tpack_t* pack, lua_State *L, int idx); +extern bool ltunpack(tpack_t* pack, lua_State *L); + +#endif diff --git a/src/lua_handle_base.cpp b/src/lua_handle_base.cpp index 9bae4a7..428a482 100644 --- a/src/lua_handle_base.cpp +++ b/src/lua_handle_base.cpp @@ -2,6 +2,7 @@ #include "network.h" #include "uv_handle_base.h" #include "lua_handle_base.h" +#include "context_lua.h" char lua_handle_base_t::m_lua_ref_table_key = 0; @@ -73,9 +74,11 @@ void lua_handle_base_t::release_ref(lua_State* L, int32_t ref, handle_set type) lua_pushlightuserdata(L, NULL); lua_rawseti(L, -3, ref); lua_pop(L, 2); - } else { + } else if (!lua_isinteger(L, -1)) { luaL_unref(L, -2, ref); lua_pop(L, 2); + } else { + lua_pop(L, 2); } } } @@ -111,7 +114,9 @@ bool lua_handle_base_t::set_option(uint8_t type, const char *option, uint8_t opt request.m_length = REQUEST_SIZE(request_handle_option_t, option_len); request.m_handle_option.m_handle = m_uv_handle; request.m_handle_option.m_option_type = type; - memcpy(REQUEST_SPARE_PTR(request.m_handle_option), option, option_len); + if (option_len > 0) { + memcpy(REQUEST_SPARE_PTR(request.m_handle_option), option, option_len); + } singleton_ref(network_t).send_request(request); return true; } @@ -155,3 +160,4 @@ int32_t lua_handle_base_t::is_closed(lua_State* L) lua_pushboolean(L, handle == NULL || handle->is_closed()); return 1; } + diff --git a/src/lua_handle_base.h b/src/lua_handle_base.h index 8dfa02a..64caf5e 100644 --- a/src/lua_handle_base.h +++ b/src/lua_handle_base.h @@ -12,9 +12,9 @@ class lua_handle_base_t virtual ~lua_handle_base_t() {}; public: - int32_t get_handle_ref() const { return m_lua_ref; }; - bool is_closed() const { return m_uv_handle == NULL; }; - handle_set get_handle_set() const { return m_handle_set; }; + FORCE_INLINE int32_t get_handle_ref() const { return m_lua_ref; }; + FORCE_INLINE bool is_closed() const { return m_uv_handle == NULL; }; + FORCE_INLINE handle_set get_handle_set() const { return m_handle_set; }; uv_handle_type get_uv_handle_type() const; virtual bool set_option(uint8_t type, const char* option, uint8_t option_len); static int32_t close(lua_State* L); diff --git a/src/lua_tcp_handle.cpp b/src/lua_tcp_handle.cpp index 7090821..9689a29 100644 --- a/src/lua_tcp_handle.cpp +++ b/src/lua_tcp_handle.cpp @@ -6,8 +6,8 @@ #include "node_lua.h" #include "uv_tcp_handle.h" -#define TCP_LISTEN_SOCKET_METATABLE "class.tcp_listen_handle_t" -#define TCP_SOCKET_METATABLE "class.tcp_socket_handle_t" +#define TCP_LISTEN_SOCKET_METATABLE "class tcp_listen_handle_t" +#define TCP_SOCKET_METATABLE "class tcp_socket_handle_t" ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -20,15 +20,23 @@ static int32_t lua_tcp_close(lua_State* L) } } -static int32_t lua_tcp_is_closed(lua_State* L) +static lua_handle_base_t* lua_check_tcp_handle(lua_State* L, int32_t idx) { lua_handle_base_t* handle; - handle = (lua_handle_base_t*)luaL_testudata(L, 1, TCP_LISTEN_SOCKET_METATABLE); + handle = (lua_handle_base_t*)luaL_testudata(L, idx, TCP_LISTEN_SOCKET_METATABLE); if (handle != NULL) { - lua_pushboolean(L, handle->is_closed()); - return 1; + return handle; + } + handle = (lua_handle_base_t*)luaL_testudata(L, idx, TCP_SOCKET_METATABLE); + if (handle != NULL) { + return handle; } - handle = (lua_handle_base_t*)luaL_testudata(L, 1, TCP_SOCKET_METATABLE); + return NULL; +} + +static int32_t lua_tcp_is_closed(lua_State* L) +{ + lua_handle_base_t* handle = lua_check_tcp_handle(L, 1); if (handle != NULL) { lua_pushboolean(L, handle->is_closed()); return 1; @@ -37,6 +45,24 @@ static int32_t lua_tcp_is_closed(lua_State* L) return 1; } +static int32_t lua_tcp_get_fd(lua_State* L) +{ + lua_handle_base_t* handle = lua_check_tcp_handle(L, 1); + if (handle != NULL) { + int64_t fd = SOCKET_MAKE_FD(handle->get_handle_ref(), context_lua_t::lua_get_context_handle(L)); + lua_pushinteger(L, fd); + return 1; + } + return 0; +} + +static int32_t lua_tcp_fd_src(lua_State* L) +{ + int64_t fd = luaL_checkinteger(L, 1); + lua_pushinteger(L, SOCKET_FD_SOURCE(fd)); + return 1; +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////// lua_tcp_listen_handle_t* lua_tcp_listen_handle_t::create_tcp_listen_socket(uv_tcp_listen_handle_t* handle, lua_State* L) @@ -53,6 +79,8 @@ lua_tcp_listen_handle_t* lua_tcp_listen_handle_t::create_tcp_listen_socket(uv_tc lua_setfield(L, -2, "close"); lua_pushcfunction(L, lua_tcp_is_closed); lua_setfield(L, -2, "is_closed"); + lua_pushcfunction(L, lua_tcp_get_fd); + lua_setfield(L, -2, "fd"); } lua_setmetatable(L, -2); return socket; @@ -66,14 +94,14 @@ int32_t lua_tcp_listen_handle_t::_listen(lua_State* L, bool ipv6) luaL_argerror(L, 1, "listen host invalid(too long)"); } uint16_t port = luaL_checkunsigned(L, 2); - uint16_t backlog = (uint16_t)-1; + bool reuse = false; int32_t top = lua_gettop(L); int32_t callback = 0; if (top >= 3) { if (lua_isfunction(L, 3)) { callback = 3; } else { - backlog = luaL_checkunsigned(L, 3); + reuse = lua_toboolean(L, 3); if (top >= 4) { luaL_checktype(L, 4, LUA_TFUNCTION); callback = 4; @@ -85,7 +113,7 @@ int32_t lua_tcp_listen_handle_t::_listen(lua_State* L, bool ipv6) request.m_type = REQUEST_TCP_LISTEN; request.m_length = REQUEST_SIZE(request_tcp_listen_t, host_len + 1); request.m_tcp_listen.m_source = lctx->get_handle(); - request.m_tcp_listen.m_backlog = backlog; + request.m_tcp_listen.m_reuse = reuse; request.m_tcp_listen.m_port = port; request.m_tcp_listen.m_ipv6 = ipv6; memcpy(REQUEST_SPARE_PTR(request.m_tcp_listen), host, host_len + 1); @@ -143,7 +171,7 @@ int32_t lua_tcp_listen_handle_t::listen_yield_finalize(lua_State *root_coro, lua return UV_OK; } -int32_t lua_tcp_listen_handle_t::listen_yield_continue(lua_State* L) +int32_t lua_tcp_listen_handle_t::listen_yield_continue(lua_State* L, int status, lua_KContext ctx) { uv_tcp_listen_handle_t* listen_handle = (uv_tcp_listen_handle_t*)lua_touserdata(L, -3); if (listen_handle) { @@ -226,7 +254,7 @@ int32_t lua_tcp_listen_handle_t::accept_yield_finalize(lua_State *root_coro, lua return UV_OK; } -int32_t lua_tcp_listen_handle_t::accept_yield_continue(lua_State* L) +int32_t lua_tcp_listen_handle_t::accept_yield_continue(lua_State* L, int status, lua_KContext ctx) { uv_tcp_socket_handle_t* accept_handle = (uv_tcp_socket_handle_t*)lua_touserdata(L, -3); if (accept_handle) { @@ -350,8 +378,20 @@ lua_tcp_socket_handle_t* lua_tcp_socket_handle_t::create_tcp_socket(uv_tcp_socke lua_setfield(L, -2, "get_rwopt"); lua_pushcfunction(L, lua_tcp_socket_handle_t::set_nodelay); lua_setfield(L, -2, "set_nodelay"); + lua_pushcfunction(L, lua_tcp_socket_handle_t::set_wshared); + lua_setfield(L, -2, "set_wshared"); + lua_pushcfunction(L, lua_tcp_socket_handle_t::get_local_addr); + lua_setfield(L, -2, "local_addr"); + lua_pushcfunction(L, lua_tcp_socket_handle_t::get_remote_addr); + lua_setfield(L, -2, "remote_addr"); + lua_pushcfunction(L, lua_tcp_socket_handle_t::get_local_port); + lua_setfield(L, -2, "local_port"); + lua_pushcfunction(L, lua_tcp_socket_handle_t::get_remote_port); + lua_setfield(L, -2, "remote_port"); lua_pushcfunction(L, lua_tcp_is_closed); lua_setfield(L, -2, "is_closed"); + lua_pushcfunction(L, lua_tcp_get_fd); + lua_setfield(L, -2, "fd"); } lua_setmetatable(L, -2); return socket; @@ -404,7 +444,7 @@ int32_t lua_tcp_socket_handle_t::_connect(lua_State* L, bool ipv6) *local_host = '\0'; /* we don't support it at this version */ if (callback > 0) { /* nonblocking callback */ lua_settop(L, callback); - request.m_tcp_connect.m_session = context_lua_t::lua_ref_callback(L, callback - 1, LUA_REFNIL, context_lua_t::common_callback_adjust); + request.m_tcp_connect.m_session = context_lua_t::lua_ref_callback(L, callback - 1, LUA_REFNIL, connect_callback_adjust); singleton_ref(network_t).send_request(request); if (timeout > 0) { context_lua_t::lua_ref_timer(L, request.m_tcp_connect.m_session, timeout, 0, false); @@ -444,7 +484,7 @@ int32_t lua_tcp_socket_handle_t::connects(lua_State* L) memcpy(REQUEST_SPARE_PTR(request.m_tcp_connects), sock, sock_len + 1); if (callback > 0) { /* nonblocking callback */ lua_settop(L, callback); - request.m_tcp_connects.m_session = context_lua_t::lua_ref_callback(L, callback - 1, LUA_REFNIL, context_lua_t::common_callback_adjust); + request.m_tcp_connects.m_session = context_lua_t::lua_ref_callback(L, callback - 1, LUA_REFNIL, connect_callback_adjust); singleton_ref(network_t).send_request(request); if (timeout > 0) { context_lua_t::lua_ref_timer(L, request.m_tcp_connects.m_session, timeout, 0, false); @@ -487,7 +527,7 @@ int32_t lua_tcp_socket_handle_t::connect_callback_adjust(lua_State* L) return 2; } -int32_t lua_tcp_socket_handle_t::connect_yield_continue(lua_State* L) +int32_t lua_tcp_socket_handle_t::connect_yield_continue(lua_State* L, int status, lua_KContext ctx) { uv_tcp_socket_handle_t* connect_handle = (uv_tcp_socket_handle_t*)lua_touserdata(L, -3); if (connect_handle) { @@ -503,6 +543,79 @@ int32_t lua_tcp_socket_handle_t::connect_yield_continue(lua_State* L) } int32_t lua_tcp_socket_handle_t::write(lua_State* L) +{ + return !lua_isinteger(L, 1) ? write_handle(L) : write_fd(L); +} + +int32_t lua_tcp_socket_handle_t::write_fd(lua_State* L) +{ + int64_t fd = luaL_checkinteger(L, 1); + const char *s = NULL; + uint32_t length; + buffer_t* buffer = NULL; + if (fd <= 0) { + return luaL_error(L, "invalid shared socket fd"); + } + if (((s = lua_tolstring(L, 2, (size_t*)&length)) == NULL) && ((buffer = (buffer_t*)luaL_testudata(L, 2, BUFFER_METATABLE)) == NULL)) { + luaL_argerror(L, 2, "string expected"); + } + if (buffer) { + length = (uint32_t)buffer_data_length(*buffer); + } + bool safety = false; + bool nonblocking = false; + if (lua_gettop(L) >= 3) { + if (lua_isfunction(L, 3)) { + nonblocking = true; + lua_settop(L, 3); + } else { + safety = lua_toboolean(L, 3); + } + } + context_lua_t* lctx = context_lua_t::lua_get_context(L); + if (length == 0) { /* empty string or buffer */ + if (nonblocking) { + int32_t session = context_lua_t::lua_ref_callback(L, 2, LUA_REFNIL, context_lua_t::common_callback_adjust); + singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), session, RESPONSE_TCP_WRITE, UV_OK); + } else if (safety) { /* real blocking mode */ + lua_pushboolean(L, true); + lua_pushinteger(L, UV_OK); + return 2; + } + return 0; + } + request_t& request = lctx->get_yielding_request(); + request.m_type = REQUEST_TCP_WRITE; + request.m_length = REQUEST_SIZE(request_tcp_write_t, 0); + request.m_tcp_write.m_socket_fd = fd; + request.m_tcp_write.m_source = lctx->get_handle(); + request.m_tcp_write.m_shared_write = true; + if (s) { + request.m_tcp_write.m_length = length; /* length > 0 */ + request.m_tcp_write.m_string = (const char*)nl_memdup(s, length); + if (!request.m_tcp_write.m_string) { + return luaL_error(L, "attempt to send data(length %d) failed: memory not enough", length); + } + } else { + request.m_tcp_write.m_length = 0; + request.m_tcp_write.m_buffer = buffer_grab(*buffer); + } + if (nonblocking) { /*nonblocking callback mode*/ + request.m_tcp_write.m_session = context_lua_t::lua_ref_callback(L, 2, LUA_REFNIL, context_lua_t::common_callback_adjust); + singleton_ref(network_t).send_request(request); + return 0; + } else { /* blocking mode */ + if (!safety) { /* wait for nothing */ + request.m_tcp_write.m_session = LUA_REFNIL; + singleton_ref(network_t).send_request(request); + return 0; + } else { /* real blocking mode */ + return lctx->lua_yield_send(L, 0, write_yield_finalize, NULL, context_lua_t::common_yield_continue); + } + } +} + +int32_t lua_tcp_socket_handle_t::write_handle(lua_State* L) { lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)luaL_checkudata(L, 1, TCP_SOCKET_METATABLE); if (socket->is_closed()) { @@ -519,7 +632,7 @@ int32_t lua_tcp_socket_handle_t::write(lua_State* L) } uv_tcp_socket_handle_t* handle = (uv_tcp_socket_handle_t*)socket->m_uv_handle; if (!check_head_option_max(handle->m_write_head_option, length)) { - return luaL_error(L, "attempt to send data(length %lu) too long(max %lu)", length, handle->m_write_head_option.max); + return luaL_error(L, "attempt to send data(length %d) too long(max %d)", length, handle->m_write_head_option.max); } bool safety = false; bool nonblocking = false; @@ -548,12 +661,14 @@ int32_t lua_tcp_socket_handle_t::write(lua_State* L) request_t& request = lctx->get_yielding_request(); request.m_type = REQUEST_TCP_WRITE; request.m_length = REQUEST_SIZE(request_tcp_write_t, 0); - request.m_tcp_write.m_socket_handle = (uv_tcp_socket_handle_t*)socket->m_uv_handle; + request.m_tcp_write.m_socket_handle = handle; + request.m_tcp_write.m_source = lctx->get_handle(); + request.m_tcp_write.m_shared_write = false; if (s) { request.m_tcp_write.m_length = length; /* length > 0 */ request.m_tcp_write.m_string = (const char*)nl_memdup(s, length); if (!request.m_tcp_write.m_string) { - return luaL_error(L, "attempt to send data(length %lu) failed: memory not enough", length); + return luaL_error(L, "attempt to send data(length %d) failed: memory not enough", length); } } else { request.m_tcp_write.m_length = 0; @@ -582,8 +697,10 @@ int32_t lua_tcp_socket_handle_t::write_yield_finalize(lua_State *root_coro, lua_ context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); request_t& request = lctx->get_yielding_request(); request.m_tcp_write.m_session = context_lua_t::lua_ref_yield_coroutine(root_coro); - uv_tcp_socket_handle_t* handle = (uv_tcp_socket_handle_t*)((lua_tcp_socket_handle_t*)userdata)->m_uv_handle; - froze_head_option(handle->m_write_head_option); + if (userdata != NULL) { + uv_tcp_socket_handle_t* handle = (uv_tcp_socket_handle_t*)((lua_tcp_socket_handle_t*)userdata)->m_uv_handle; + froze_head_option(handle->m_write_head_option); + } singleton_ref(network_t).send_request(request); } else { //yield failed, clear your data in case of memory leak. context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); @@ -618,18 +735,6 @@ int32_t lua_tcp_socket_handle_t::read(lua_State* L) } /* error already occurred in network thread, stop read immediately. */ context_lua_t* lctx = context_lua_t::lua_get_context(L); - uv_err_code err = handle->get_read_error(); - if (err != UV_OK) { - if (nonblocking) { - socket->m_read_ref_sessions.make_nonblocking_callback(L, 1, context_lua_t::common_callback_adjust); - singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_TCP_READ, err); /* The message will wake up all blocking coroutine and nonblocking callback. */ - return 0; - } else { - lua_pushboolean(L, 0); - lua_pushinteger(L, err); - return 2; - } - } request_t& request = lctx->get_yielding_request(); request.m_type = REQUEST_TCP_READ; request.m_length = REQUEST_SIZE(request_tcp_read_t, 0); @@ -638,11 +743,11 @@ int32_t lua_tcp_socket_handle_t::read(lua_State* L) if (nonblocking) { /* nonblocking callback */ if (socket->m_read_ref_sessions.make_nonblocking_callback(L, 1, context_lua_t::common_callback_adjust)) { froze_head_option(handle->m_read_head_option); - if (socket->m_read_overflow_buffers.empty()) { + if (handle->get_read_error() == UV_OK) { singleton_ref(network_t).send_request(request); - } else { - singleton_ref(node_lua_t).context_send_buffer_release(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_TCP_READ, socket->m_read_overflow_buffers.front()); - socket->m_read_overflow_buffers.pop(); + } + if (!socket->m_read_overflow_buffers.empty()) { //do read over flow wake up(whether read error occurred or not)!!! + singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_TCP_READ, NL_ETCPREADOVERFLOW); } } return 0; @@ -659,15 +764,21 @@ int32_t lua_tcp_socket_handle_t::read_yield_finalize(lua_State *root_coro, lua_S int32_t session = socket->m_read_ref_sessions.push_blocking(root_coro); uv_tcp_socket_handle_t* handle = (uv_tcp_socket_handle_t*)socket->m_uv_handle; froze_head_option(handle->m_read_head_option); - if (socket->m_read_overflow_buffers.empty()) { - singleton_ref(network_t).send_request(lctx->get_yielding_request()); + if (handle->get_read_error() == UV_OK) { + if (socket->m_read_timeout_session_count == 0) { + singleton_ref(network_t).send_request(lctx->get_yielding_request()); + } else { + --socket->m_read_timeout_session_count; + } int64_t timeout = lctx->get_yielding_timeout(); if (timeout > 0) { context_lua_t::lua_ref_timer(main_coro, session, timeout, 0, false, socket, read_timeout); } - } else { - singleton_ref(node_lua_t).context_send_buffer_release(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_TCP_READ, socket->m_read_overflow_buffers.front()); - socket->m_read_overflow_buffers.pop(); + if (!socket->m_read_overflow_buffers.empty()) { //do read over flow wake up(whether read error occurred or not)!!! + singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_TCP_READ, NL_ETCPREADOVERFLOW); + } + } else { //wake up the blocking read directly + singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_TCP_READ, NL_ETCPREADOVERFLOW); } } return UV_OK; @@ -678,7 +789,11 @@ int32_t lua_tcp_socket_handle_t::read_timeout(lua_State *L, int32_t session, voi lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)userdata; context_lua_t* lctx = context_lua_t::lua_get_context(L); message_t message(0, session, RESPONSE_TCP_READ, NL_ETIMEOUT); - return socket->m_read_ref_sessions.wakeup_one_fixed(lctx, message, false); + if (socket->m_read_ref_sessions.wakeup_one_fixed(lctx, message, false)) { + ++socket->m_read_timeout_session_count; + return 1; + } + return 0; } static void opt_check_endian(lua_State* L, int32_t idx, const char* field, char *value, bool frozen) @@ -686,7 +801,7 @@ static void opt_check_endian(lua_State* L, int32_t idx, const char* field, char lua_getfield(L, idx, field); if (!lua_isnil(L, -1)) { size_t len = 0; - const char* head_endian = luaL_tolstring(L, -1, &len); + const char* head_endian = lua_tolstring(L, -1, &len); if (len != 1 || (*head_endian != 'L' && *head_endian != 'B')) { luaL_error(L, "option table error, field '%s' not correct", field); } @@ -806,13 +921,99 @@ int32_t lua_tcp_socket_handle_t::get_rwopt(lua_State* L) int32_t lua_tcp_socket_handle_t::set_nodelay(lua_State* L) { - lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)luaL_testudata(L, 1, TCP_SOCKET_METATABLE); + lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)luaL_checkudata(L, 1, TCP_SOCKET_METATABLE); if (!socket->is_closed() && socket->get_uv_handle_type() == UV_TCP) { - socket->set_option(OPT_TCP_NODELAY, lua_toboolean(L, 2) ? "\x01\0" : "\0\0", 2); + socket->set_option(OPT_TCP_NODELAY, lua_toboolean(L, 2) ? "\x01\0" : "\x00\0", 2); + } + return 0; +} + +int32_t lua_tcp_socket_handle_t::set_wshared(lua_State* L) +{ + lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)luaL_checkudata(L, 1, TCP_SOCKET_METATABLE); + if (!socket->is_closed()) { + bool enable = lua_toboolean(L, 2); + socket->set_option(OPT_TCP_WSHARED, enable ? "\x01\0" : "\x00\0", 2); + if (enable) { + uv_tcp_socket_handle_t* handle = (uv_tcp_socket_handle_t*)socket->m_uv_handle; + froze_head_option(handle->m_write_head_option); + } } return 0; } +static lua_tcp_socket_handle_t* check_socket_addr(lua_State* L, int32_t idx) +{ + lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)luaL_checkudata(L, idx, TCP_SOCKET_METATABLE); + if (socket->is_closed()) { + luaL_error(L, "attempt to perform on a invalid or closed socket"); + } + if (socket->get_uv_handle_type() == UV_NAMED_PIPE) { +#if defined CC_MSVC + luaL_error(L, "attempt to perform on a named pipe"); +#else + luaL_error(L, "attempt to perform on a unix domain socket"); +#endif + } + return socket; +} + +int32_t lua_tcp_socket_handle_t::get_local_addr(lua_State* L) +{ + lua_tcp_socket_handle_t* socket = check_socket_addr(L, 1); + uv_os_sock_t sock = ((uv_tcp_socket_handle_t*)socket->m_uv_handle)->m_tcp_sock; + char host[512] = { '\0' }; + bool ipv6 = false; + if (socket_host(sock, true, host, sizeof(host), &ipv6, NULL)) { + lua_pushstring(L, host); + lua_pushboolean(L, ipv6); + } else { + lua_pushstring(L, "unknown address"); + lua_pushboolean(L, ipv6); + } + return 2; +} + +int32_t lua_tcp_socket_handle_t::get_remote_addr(lua_State* L) +{ + lua_tcp_socket_handle_t* socket = check_socket_addr(L, 1); + uv_os_sock_t sock = ((uv_tcp_socket_handle_t*)socket->m_uv_handle)->m_tcp_sock; + char host[512] = { '\0' }; + bool ipv6 = false; + if (socket_host(sock, false, host, sizeof(host), &ipv6, NULL)) { + lua_pushstring(L, host); + } else { + lua_pushstring(L, "unknown address"); + } + return 1; +} + +int32_t lua_tcp_socket_handle_t::get_local_port(lua_State* L) +{ + lua_tcp_socket_handle_t* socket = check_socket_addr(L, 1); + uv_os_sock_t sock = ((uv_tcp_socket_handle_t*)socket->m_uv_handle)->m_tcp_sock; + uint16_t port; + if (socket_host(sock, true, NULL, 0, NULL, &port)) { + lua_pushinteger(L, port); + } else { + lua_pushinteger(L, -1); + } + return 1; +} + +int32_t lua_tcp_socket_handle_t::get_remote_port(lua_State* L) +{ + lua_tcp_socket_handle_t* socket = check_socket_addr(L, 1); + uv_os_sock_t sock = ((uv_tcp_socket_handle_t*)socket->m_uv_handle)->m_tcp_sock; + uint16_t port; + if (socket_host(sock, false, NULL, 0, NULL, &port)) { + lua_pushinteger(L, port); + } else { + lua_pushinteger(L, -1); + } + return 1; +} + int32_t lua_tcp_socket_handle_t::wakeup_connect(lua_State* L, message_t& message) { context_lua_t* lctx = context_lua_t::lua_get_context(L); @@ -831,15 +1032,29 @@ int32_t lua_tcp_socket_handle_t::wakeup_read(lua_State* L, message_t& message) lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)get_lua_handle(L, message.m_session, SOCKET_SET); if (socket) { if (!socket->is_closed()) { - if (message_is_buffer(message)) { - if (socket->m_read_ref_sessions.wakeup_once(lctx, message)) { + if (socket->m_read_overflow_buffers.empty()) { + if (message_is_buffer(message)) { + if (socket->m_read_ref_sessions.wakeup_once(lctx, message)) { + return 1; + } + socket->m_read_overflow_buffers.push(buffer_grab(message_buffer(message))); + return 0; + } //else empty overflow buffers + goto handle_error; + } else { + if (message_is_buffer(message)) { + socket->m_read_overflow_buffers.push(buffer_grab(message_buffer(message))); + } + while (!socket->m_read_ref_sessions.is_empty() && !socket->m_read_overflow_buffers.empty()) { + message_t overflow_message(0, socket->m_lua_ref, RESPONSE_TCP_READ, socket->m_read_overflow_buffers.front()); + socket->m_read_overflow_buffers.pop(); + socket->m_read_ref_sessions.wakeup_once(lctx, overflow_message); + buffer_release(message_buffer(overflow_message)); + } + if (!socket->m_read_overflow_buffers.empty()) { return 1; } - socket->m_read_overflow_buffers.push(buffer_grab(message_buffer(message))); - } - if (message_is_error(message)) { - socket->m_read_ref_sessions.wakeup_all(lctx, message); - return 1; + goto handle_error; } return 0; } else { /* listen socket is already closed */ @@ -850,6 +1065,21 @@ int32_t lua_tcp_socket_handle_t::wakeup_read(lua_State* L, message_t& message) } } return 0; + +handle_error: + if (message_is_error(message)) { + uv_err_code err = ((uv_tcp_socket_handle_t*)socket->m_uv_handle)->get_read_error(); + if (err != UV_OK || message_error(message) != NL_ETCPREADOVERFLOW) { //if it's over flow wakeup, do nothing!!! + if (message_error(message) != NL_ETCPREADOVERFLOW) { + socket->m_read_ref_sessions.wakeup_all(lctx, message); + } else { + message_t error = message_t(0, socket->m_lua_ref, RESPONSE_TCP_READ, err); + socket->m_read_ref_sessions.wakeup_all(lctx, error); + } + return 1; + } + } + return 0; } int32_t lua_tcp_socket_handle_t::close(lua_State* L) @@ -857,7 +1087,7 @@ int32_t lua_tcp_socket_handle_t::close(lua_State* L) lua_tcp_socket_handle_t* socket = (lua_tcp_socket_handle_t*)luaL_checkudata(L, 1, TCP_SOCKET_METATABLE); if (!socket->is_closed()) { context_lua_t* lctx = context_lua_t::lua_get_context(L); - /* The message will wake up all blocking coroutine only*/ + /* The message will wake up all blocking coroutine only and close the nonblocking lua ref */ singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_TCP_CLOSING, NL_ETCPSCLOSED); socket->_close(); socket->release_read_overflow_buffers(); @@ -870,7 +1100,8 @@ int32_t lua_tcp_socket_handle_t::close(lua_State* L) void lua_tcp_socket_handle_t::release_read_overflow_buffers() { - for (int32_t i = 0; i < m_read_overflow_buffers.size(); ++i) { + int32_t size = m_read_overflow_buffers.size(); + for (int32_t i = 0; i < size; ++i) { buffer_release(m_read_overflow_buffers.front()); m_read_overflow_buffers.pop(); } @@ -878,7 +1109,7 @@ void lua_tcp_socket_handle_t::release_read_overflow_buffers() int luaopen_tcp(lua_State *L) { - luaL_Reg l[] = { + static const luaL_Reg l[] = { { "listen", lua_tcp_listen_handle_t::listen }, { "listen6", lua_tcp_listen_handle_t::listen6 }, { "listens", lua_tcp_listen_handle_t::listens }, @@ -891,8 +1122,15 @@ int luaopen_tcp(lua_State *L) { "set_rwopt", lua_tcp_socket_handle_t::set_rwopt }, { "get_rwopt", lua_tcp_socket_handle_t::get_rwopt }, { "set_nodelay", lua_tcp_socket_handle_t::set_nodelay }, + { "set_wshared", lua_tcp_socket_handle_t::set_wshared }, + { "local_addr", lua_tcp_socket_handle_t::get_local_addr }, + { "remote_addr", lua_tcp_socket_handle_t::get_remote_addr }, + { "local_port", lua_tcp_socket_handle_t::get_local_port }, + { "remote_port", lua_tcp_socket_handle_t::get_remote_port }, { "close", lua_tcp_close }, { "is_closed", lua_tcp_is_closed }, + { "fd", lua_tcp_get_fd }, + { "fd_src", lua_tcp_fd_src }, { NULL, NULL }, }; luaL_newlib(L, l); diff --git a/src/lua_tcp_handle.h b/src/lua_tcp_handle.h index 3852e4b..ba02896 100644 --- a/src/lua_tcp_handle.h +++ b/src/lua_tcp_handle.h @@ -30,8 +30,8 @@ class lua_tcp_listen_handle_t : public lua_handle_base_t static int32_t accept_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); static int32_t listen_callback_adjust(lua_State* L); static int32_t accept_callback_adjust(lua_State* L); - static int32_t listen_yield_continue(lua_State* L); - static int32_t accept_yield_continue(lua_State* L); + static int32_t listen_yield_continue(lua_State* L, int status, lua_KContext ctx); + static int32_t accept_yield_continue(lua_State* L, int status, lua_KContext ctx); static int32_t accept_timeout(lua_State *L, int32_t session, void* userdata, bool is_repeat); private: @@ -47,7 +47,8 @@ class lua_tcp_socket_handle_t : public lua_handle_base_t { public: lua_tcp_socket_handle_t(uv_tcp_socket_handle_t* handle, lua_State* L) - : lua_handle_base_t((uv_handle_base_t*)handle, L) {} + : lua_handle_base_t((uv_handle_base_t*)handle, L), + m_read_timeout_session_count(0) {} ~lua_tcp_socket_handle_t(); public: @@ -59,14 +60,21 @@ class lua_tcp_socket_handle_t : public lua_handle_base_t static int32_t set_rwopt(lua_State* L); static int32_t get_rwopt(lua_State* L); static int32_t set_nodelay(lua_State* L); + static int32_t set_wshared(lua_State* L); + static int32_t get_local_addr(lua_State* L); + static int32_t get_remote_addr(lua_State* L); + static int32_t get_local_port(lua_State* L); + static int32_t get_remote_port(lua_State* L); static int32_t close(lua_State* L); private: static int32_t _connect(lua_State* L, bool ipv6); static int32_t connect_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); + static int32_t write_handle(lua_State* L); + static int32_t write_fd(lua_State* L); static int32_t write_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); static int32_t read_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); static int32_t connect_callback_adjust(lua_State* L); - static int32_t connect_yield_continue(lua_State* L); + static int32_t connect_yield_continue(lua_State* L, int status, lua_KContext ctx); static int32_t read_timeout(lua_State *L, int32_t session, void* userdata, bool is_repeat); public: @@ -77,6 +85,7 @@ class lua_tcp_socket_handle_t : public lua_handle_base_t private: void release_read_overflow_buffers(); private: + int32_t m_read_timeout_session_count; //blocking ref_sessions_t m_read_ref_sessions; std::queue m_read_overflow_buffers; }; diff --git a/src/lua_timer_handle.cpp b/src/lua_timer_handle.cpp index df1335c..0e54bfc 100644 --- a/src/lua_timer_handle.cpp +++ b/src/lua_timer_handle.cpp @@ -4,7 +4,7 @@ #include "context_lua.h" #include "network.h" -#define TIMER_METATABLE "class.timer_handle_t" +#define TIMER_METATABLE "class timer_handle_t" int32_t lua_timer_handle_t::sleep(lua_State* L) { @@ -26,7 +26,7 @@ int32_t lua_timer_handle_t::sleep_yield_finalize(lua_State *root_coro, lua_State return UV_OK; } -int32_t lua_timer_handle_t::sleep_yield_continue(lua_State* L) +int32_t lua_timer_handle_t::sleep_yield_continue(lua_State* L, int status, lua_KContext ctx) { return 0; } @@ -119,7 +119,7 @@ int32_t lua_timer_handle_t::wakeup(lua_State* L, message_t& message) int luaopen_timer(lua_State *L) { - luaL_Reg l[] = { + static const luaL_Reg l[] = { { "sleep", lua_timer_handle_t::sleep }, { "timeout", lua_timer_handle_t::timeout }, { "close", lua_timer_handle_t::close }, diff --git a/src/lua_timer_handle.h b/src/lua_timer_handle.h index 716b87f..cbff549 100644 --- a/src/lua_timer_handle.h +++ b/src/lua_timer_handle.h @@ -38,7 +38,7 @@ class lua_timer_handle_t : public lua_handle_base_t private: static int32_t sleep_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); - static int32_t sleep_yield_continue(lua_State* L); + static int32_t sleep_yield_continue(lua_State* L, int status, lua_KContext ctx); static int32_t timer_callback_adjust(lua_State* L); public: diff --git a/src/lua_udp_handle.cpp b/src/lua_udp_handle.cpp new file mode 100644 index 0000000..1280175 --- /dev/null +++ b/src/lua_udp_handle.cpp @@ -0,0 +1,454 @@ +#include "lua_udp_handle.h" +#include "request.h" +#include "context_lua.h" +#include "network.h" +#include "lbuffer.h" +#include "node_lua.h" +#include "uv_udp_handle.h" + +#define UDP_SOCKET_METATABLE "class udp_socket_handle_t" + +lua_udp_handle_t* lua_udp_handle_t::create_udp_socket(uv_udp_handle_t* handle, lua_State* L) +{ + lua_udp_handle_t* socket = new(lua_newuserdata(L, sizeof(lua_udp_handle_t)))lua_udp_handle_t(handle, L); + if (luaL_newmetatable(L, UDP_SOCKET_METATABLE)) { /* create new metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lua_udp_handle_t::release); + lua_setfield(L, -2, "__gc"); + lua_pushcfunction(L, lua_udp_handle_t::write); + lua_setfield(L, -2, "write"); + lua_pushcfunction(L, lua_udp_handle_t::write6); + lua_setfield(L, -2, "write6"); + lua_pushcfunction(L, lua_udp_handle_t::read); + lua_setfield(L, -2, "read"); + lua_pushcfunction(L, lua_udp_handle_t::set_wshared); + lua_setfield(L, -2, "set_wshared"); + lua_pushcfunction(L, lua_udp_handle_t::get_local_addr); + lua_setfield(L, -2, "local_addr"); + lua_pushcfunction(L, lua_udp_handle_t::get_remote_addr); + lua_setfield(L, -2, "remote_addr"); + lua_pushcfunction(L, lua_udp_handle_t::get_local_port); + lua_setfield(L, -2, "local_port"); + lua_pushcfunction(L, lua_udp_handle_t::get_remote_port); + lua_setfield(L, -2, "remote_port"); + lua_pushcfunction(L, lua_udp_handle_t::close); + lua_setfield(L, -2, "close"); + lua_pushcfunction(L, lua_udp_handle_t::udp_is_closed); + lua_setfield(L, -2, "is_closed"); + lua_pushcfunction(L, lua_udp_handle_t::get_fd); + lua_setfield(L, -2, "fd"); + } + lua_setmetatable(L, -2); + return socket; +} + +int32_t lua_udp_handle_t::open(lua_State* L) +{ + return _open(L, false); +} + +int32_t lua_udp_handle_t::open6(lua_State* L) +{ + return _open(L, true); +} + +int32_t lua_udp_handle_t::open_callback_adjust(lua_State* L) +{ + uv_udp_handle_t* handle = (uv_udp_handle_t*)lua_touserdata(L, -3); + if (handle) { + lua_pop(L, 3); + create_udp_socket(handle, L); + } else { + lua_pop(L, 2); + } + return 2; +} + +int32_t lua_udp_handle_t::open_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination) +{ + if (root_coro != NULL) { + context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); + request_t& request = lctx->get_yielding_request(); + request.m_udp_open.m_session = context_lua_t::lua_ref_yield_coroutine(root_coro); + singleton_ref(network_t).send_request(request); + } + return UV_OK; +} + +int32_t lua_udp_handle_t::open_yield_continue(lua_State* L, int status, lua_KContext ctx) +{ + uv_udp_handle_t* handle = (uv_udp_handle_t*)lua_touserdata(L, -3); + if (handle) { + lua_pop(L, 3); + create_udp_socket(handle, L); + } else { + lua_pop(L, 2); + if (context_lua_t::lua_get_context(L)->get_yielding_status() != UV_OK) { + context_lua_t::lua_throw(L, -1); + } + } + return 2; +} + +int32_t lua_udp_handle_t::_open(lua_State* L, bool ipv6) +{ + const char* host = NULL; + size_t host_len; + uint16_t port; + int32_t callback = false; + int32_t top = lua_gettop(L); + if (!lua_isfunction(L, 1) && top > 0) { + host = luaL_checklstring(L, 1, &host_len); + port = luaL_checkunsigned(L, 2); + if (host_len + 1 > REQUEST_SPARE_SIZE(request_udp_open_t)) { + luaL_argerror(L, 1, "udp open host invalid(too long)"); + } + if (lua_gettop(L) >= 3) { + luaL_checktype(L, 3, LUA_TFUNCTION); + callback = 3; + } + } else { + host = !ipv6 ? "0.0.0.0" : "::"; + host_len = !ipv6 ? 7 : 2; + port = 0; + callback = top > 0 ? 1 : 0; + } + context_lua_t* lctx = context_lua_t::lua_get_context(L); + request_t& request = lctx->get_yielding_request(); + request.m_type = REQUEST_UDP_OPEN; + request.m_length = REQUEST_SIZE(request_udp_open_t, host_len + 1); + request.m_udp_open.m_source = lctx->get_handle(); + request.m_udp_open.m_port = port; + request.m_udp_open.m_ipv6 = ipv6; + memcpy(REQUEST_SPARE_PTR(request.m_udp_open), host, host_len + 1); + if (callback > 0) { /* nonblocking callback */ + lua_settop(L, callback); + request.m_udp_open.m_session = context_lua_t::lua_ref_callback(L, callback - 1, LUA_REFNIL, open_callback_adjust); + singleton_ref(network_t).send_request(request); + return 0; + } else { /* blocking */ + return lctx->lua_yield_send(L, 0, open_yield_finalize, NULL, open_yield_continue); + } +} + +int32_t lua_udp_handle_t::write(lua_State* L) +{ + return _write(L, false); +} + +int32_t lua_udp_handle_t::write6(lua_State* L) +{ + return _write(L, true); +} + +int32_t lua_udp_handle_t::_write(lua_State* L, bool ipv6) +{ + lua_udp_handle_t* socket = NULL; + int64_t fd = 0; + const char *s = NULL; + uint32_t length; + buffer_t* buffer = NULL; + const char* host; + size_t host_len; + uint16_t port; + if (!lua_isinteger(L, 1)) { + socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (socket->is_closed()) { + return luaL_error(L, "attempt to send data on a invalid or closed socket"); + } + } else { + fd = lua_tointeger(L, 1); + } + if (((s = lua_tolstring(L, 2, (size_t*)&length)) == NULL) && ((buffer = (buffer_t*)luaL_testudata(L, 2, BUFFER_METATABLE)) == NULL)) { + luaL_argerror(L, 2, "string expected"); + } + if (buffer) { + length = (uint32_t)buffer_data_length(*buffer); + } + host = luaL_checklstring(L, 3, &host_len); + port = luaL_checkunsigned(L, 4); + if (host_len + 1 > REQUEST_SPARE_SIZE(request_udp_write_t)) { + luaL_argerror(L, 3, "udp send host invalid(too long)"); + } + bool safety = false; + bool nonblocking = false; + if (lua_gettop(L) >= 5) { + if (lua_isfunction(L, 5)) { + nonblocking = true; + lua_settop(L, 5); + } else { + safety = lua_toboolean(L, 5); + } + } + context_lua_t* lctx = context_lua_t::lua_get_context(L); + if (length == 0) { /* empty string or buffer */ + if (nonblocking) { + int32_t session = context_lua_t::lua_ref_callback(L, 4, LUA_REFNIL, context_lua_t::common_callback_adjust); + singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), session, RESPONSE_UDP_WRITE, UV_OK); + } else if (safety) { /* real blocking mode */ + lua_pushboolean(L, true); + lua_pushinteger(L, UV_OK); + return 2; + } + return 0; + } + request_t& request = lctx->get_yielding_request(); + request.m_type = REQUEST_UDP_WRITE; + request.m_length = REQUEST_SIZE(request_udp_write_t, host_len + 1); + if (socket != NULL) { + request.m_udp_write.m_socket_handle = (uv_udp_handle_t*)socket->m_uv_handle; + request.m_udp_write.m_shared_write = false; + } else { + request.m_udp_write.m_socket_fd = fd; + request.m_udp_write.m_shared_write = true; + } + request.m_udp_write.m_source = lctx->get_handle(); + request.m_udp_write.m_port = port; + request.m_udp_write.m_ipv6 = ipv6; + memcpy(REQUEST_SPARE_PTR(request.m_udp_write), host, host_len + 1); + if (s) { + request.m_udp_write.m_length = length; /* length > 0 */ + request.m_udp_write.m_string = (const char*)nl_memdup(s, length); + if (!request.m_udp_write.m_string) { + return luaL_error(L, "attempt to send data(length %d) failed: memory not enough", length); + } + } else { + request.m_udp_write.m_length = 0; + request.m_udp_write.m_buffer = buffer_grab(*buffer); + } + if (nonblocking) { /*nonblocking callback mode*/ + request.m_udp_write.m_session = context_lua_t::lua_ref_callback(L, 4, LUA_REFNIL, context_lua_t::common_callback_adjust); + singleton_ref(network_t).send_request(request); + return 0; + } else { /* blocking mode */ + if (!safety) { /* wait for nothing */ + request.m_udp_write.m_session = LUA_REFNIL; + singleton_ref(network_t).send_request(request); + return 0; + } else { /* real blocking mode */ + return lctx->lua_yield_send(L, 0, write_yield_finalize, NULL, context_lua_t::common_yield_continue); + } + } +} + +int32_t lua_udp_handle_t::write_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination) +{ + if (root_coro != NULL) { //yield succeeded + context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); + request_t& request = lctx->get_yielding_request(); + request.m_udp_write.m_session = context_lua_t::lua_ref_yield_coroutine(root_coro); + singleton_ref(network_t).send_request(request); + } else { //yield failed, clear your data in case of memory leak. + context_lua_t* lctx = context_lua_t::lua_get_context(root_coro); + request_t& request = lctx->get_yielding_request(); + if (request.m_udp_write.m_length > 0) { + nl_free((void*)request.m_udp_write.m_string); + request.m_udp_write.m_string = NULL; + request.m_udp_write.m_length = 0; + } else { + buffer_release(request.m_udp_write.m_buffer); + } + } + return UV_OK; +} + +int32_t lua_udp_handle_t::read(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (socket->is_closed()) { + return luaL_error(L, "attempt to read data on a invalid or closed socket"); + } + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_settop(L, 2); + socket->m_nonblocking_ref = context_lua_t::lua_ref_callback(L, 1, socket->m_nonblocking_ref, read_callback_adjust); + context_lua_t* lctx = context_lua_t::lua_get_context(L); + request_t& request = lctx->get_yielding_request(); + request.m_type = REQUEST_UDP_READ; + request.m_length = REQUEST_SIZE(request_udp_read_t, 0); + request.m_udp_read.m_socket_handle = (uv_udp_handle_t*)socket->m_uv_handle; + singleton_ref(network_t).send_request(request); + return 0; +} + +int32_t lua_udp_handle_t::read_callback_adjust(lua_State* L) +{ + lua_pop(L, 2); + for (int32_t i = lua_gettop(L); i < 5; ++i) { + lua_pushnil(L); + } + return 5; +} + +int32_t lua_udp_handle_t::close(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (!socket->is_closed()) { + context_lua_t* lctx = context_lua_t::lua_get_context(L); + /* The message will close the nonblocking lua ref */ + singleton_ref(node_lua_t).context_send(lctx, lctx->get_handle(), socket->m_lua_ref, RESPONSE_UDP_CLOSING, NL_EUDPSCLOSED); + socket->_close(); + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 1; +} + +int32_t lua_udp_handle_t::set_wshared(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (!socket->is_closed()) { + bool enable = lua_toboolean(L, 2); + socket->set_option(OPT_UDP_WSHARED, enable ? "\x01\0" : "\x00\0", 2); + } + return 0; +} + +int32_t lua_udp_handle_t::udp_is_closed(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (socket != NULL) { + lua_pushboolean(L, socket->is_closed()); + return 1; + } + lua_pushboolean(L, 0); + return 1; +} + +int32_t lua_udp_handle_t::get_local_addr(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (socket->is_closed()) { + luaL_error(L, "attempt to perform on a invalid or closed socket"); + } + uv_os_sock_t sock = ((uv_udp_handle_t*)socket->m_uv_handle)->m_udp_sock; + char host[512] = { '\0' }; + bool ipv6 = false; + if (socket_host(sock, true, host, sizeof(host), &ipv6, NULL)) { + lua_pushstring(L, host); + lua_pushboolean(L, ipv6); + } else { + lua_pushstring(L, "unknown address"); + lua_pushboolean(L, ipv6); + } + return 2; +} + +int32_t lua_udp_handle_t::get_remote_addr(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (socket->is_closed()) { + luaL_error(L, "attempt to perform on a invalid or closed socket"); + } + uv_os_sock_t sock = ((uv_udp_handle_t*)socket->m_uv_handle)->m_udp_sock; + char host[512] = { '\0' }; + bool ipv6 = false; + if (socket_host(sock, false, host, sizeof(host), &ipv6, NULL)) { + lua_pushstring(L, host); + } else { + lua_pushstring(L, "unknown address"); + } + return 1; +} + +int32_t lua_udp_handle_t::get_local_port(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (socket->is_closed()) { + luaL_error(L, "attempt to perform on a invalid or closed socket"); + } + uv_os_sock_t sock = ((uv_udp_handle_t*)socket->m_uv_handle)->m_udp_sock; + uint16_t port; + if (socket_host(sock, true, NULL, 0, NULL, &port)) { + lua_pushinteger(L, port); + } else { + lua_pushinteger(L, -1); + } + return 1; +} + +int32_t lua_udp_handle_t::get_remote_port(lua_State* L) +{ + lua_udp_handle_t* socket = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (socket->is_closed()) { + luaL_error(L, "attempt to perform on a invalid or closed socket"); + } + uv_os_sock_t sock = ((uv_udp_handle_t*)socket->m_uv_handle)->m_udp_sock; + uint16_t port; + if (socket_host(sock, false, NULL, 0, NULL, &port)) { + lua_pushinteger(L, port); + } else { + lua_pushinteger(L, -1); + } + return 1; +} + +int32_t lua_udp_handle_t::get_fd(lua_State* L) +{ + lua_udp_handle_t* handle = (lua_udp_handle_t*)luaL_checkudata(L, 1, UDP_SOCKET_METATABLE); + if (handle != NULL) { + int64_t fd = SOCKET_MAKE_FD(handle->get_handle_ref(), context_lua_t::lua_get_context_handle(L)); + lua_pushinteger(L, fd); + return 1; + } + return 0; +} + +int32_t lua_udp_handle_t::wakeup_open(lua_State* L, message_t& message) +{ + context_lua_t* lctx = context_lua_t::lua_get_context(L); + if (!lctx->wakeup_ref_session(message.m_session, message, true)) { + if (message_is_userdata(message)) { + close_uv_handle((uv_handle_base_t*)message_userdata(message)); + } + return 0; + } + return 1; +} + +int32_t lua_udp_handle_t::wakeup_read(lua_State* L, message_t& message) +{ + context_lua_t* lctx = context_lua_t::lua_get_context(L); + lua_udp_handle_t* socket = (lua_udp_handle_t*)get_lua_handle(L, message.m_session, SOCKET_SET); + if (socket) { + if (!socket->is_closed()) { + return lctx->wakeup_ref_session(socket->m_nonblocking_ref, message, false); + } else if (socket->m_nonblocking_ref != LUA_REFNIL) { /* udp socket is already closed */ + context_lua_t::lua_free_ref_session(L, socket->m_nonblocking_ref); + socket->m_nonblocking_ref = LUA_REFNIL; + return 0; + } + } + return 0; +} + +static int32_t lua_udp_fd_src(lua_State* L) +{ + int64_t fd = luaL_checkinteger(L, 1); + lua_pushinteger(L, SOCKET_FD_SOURCE(fd)); + return 1; +} + +int luaopen_udp(lua_State *L) +{ + static const luaL_Reg l[] = { + { "open", lua_udp_handle_t::open }, + { "open6", lua_udp_handle_t::open6 }, + { "write", lua_udp_handle_t::write }, + { "write6", lua_udp_handle_t::write6 }, + { "read", lua_udp_handle_t::read }, + { "set_wshared", lua_udp_handle_t::set_wshared }, + { "local_addr", lua_udp_handle_t::get_local_addr }, + { "remote_addr", lua_udp_handle_t::get_remote_addr }, + { "local_port", lua_udp_handle_t::get_local_port }, + { "remote_port", lua_udp_handle_t::get_remote_port }, + { "close", lua_udp_handle_t::close }, + { "is_closed", lua_udp_handle_t::udp_is_closed }, + { "fd", lua_udp_handle_t::get_fd }, + { "fd_src", lua_udp_fd_src }, + { NULL, NULL }, + }; + luaL_newlib(L, l); + return 1; +} diff --git a/src/lua_udp_handle.h b/src/lua_udp_handle.h new file mode 100644 index 0000000..c15b2ae --- /dev/null +++ b/src/lua_udp_handle.h @@ -0,0 +1,45 @@ +#ifndef LUA_UDP_HANDLE_H_ +#define LUA_UDP_HANDLE_H_ +#include "common.h" +#include "message.h" +#include "lua_handle_base.h" +#include "context_lua.h" + +class lua_udp_handle_t : public lua_handle_base_t +{ +public: + lua_udp_handle_t(uv_udp_handle_t* handle, lua_State* L) + : lua_handle_base_t((uv_handle_base_t*)handle, L), + m_nonblocking_ref(LUA_REFNIL) {} + ~lua_udp_handle_t() {} +private: + int32_t m_nonblocking_ref; /* nonblocking_ref callback ref */ +public: + static int32_t open(lua_State* L); + static int32_t open6(lua_State* L); + static int32_t write(lua_State* L); + static int32_t write6(lua_State* L); + static int32_t read(lua_State* L); + static int32_t close(lua_State* L); + static int32_t udp_is_closed(lua_State* L); + static int32_t set_wshared(lua_State* L); + static int32_t get_local_addr(lua_State* L); + static int32_t get_remote_addr(lua_State* L); + static int32_t get_local_port(lua_State* L); + static int32_t get_remote_port(lua_State* L); + static int32_t get_fd(lua_State* L); +private: + static int32_t _open(lua_State* L, bool ipv6); + static int32_t open_callback_adjust(lua_State* L); + static int32_t open_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); + static int32_t open_yield_continue(lua_State* L, int status, lua_KContext ctx); + static int32_t _write(lua_State* L, bool ipv6); + static int32_t write_yield_finalize(lua_State *root_coro, lua_State *main_coro, void *userdata, uint32_t destination); + static int32_t read_callback_adjust(lua_State* L); +public: + static lua_udp_handle_t* create_udp_socket(uv_udp_handle_t* handle, lua_State* L); + static int32_t wakeup_open(lua_State* L, message_t& message); + static int32_t wakeup_read(lua_State* L, message_t& message); +}; + +#endif \ No newline at end of file diff --git a/src/message.cpp b/src/message.cpp new file mode 100644 index 0000000..1e8cce8 --- /dev/null +++ b/src/message.cpp @@ -0,0 +1,17 @@ +#include "message.h" + +message_array_t* message_array_create(uint32_t count) { + message_array_t* array = (message_array_t*)nl_calloc(sizeof(message_array_t) + (count - 1)*sizeof(message_t)); + array->m_count = count; + return array; +} + +void message_array_release(message_array_t* array) { + if (array != NULL) { + for (uint32_t i = 0; i < array->m_count; ++i) { + message_clean(array->m_array[i]); + } + nl_free(array); + } +} + diff --git a/src/message.h b/src/message.h index 953b94e..cedd146 100644 --- a/src/message.h +++ b/src/message.h @@ -1,6 +1,9 @@ #ifndef MESSAGE_H_ #define MESSAGE_H_ #include "buffer.h" +#include "lbinary.h" +#include "ltpack.h" +#include "lshared.h" #define MESSAGE_TYPE_BIT 24 #define MAKE_MESSAGE_TYPE(mtype, dtype) (((uint32_t)(dtype) << MESSAGE_TYPE_BIT) | ((mtype) & (((uint32_t)1 << MESSAGE_TYPE_BIT) - 1))) @@ -8,24 +11,35 @@ #define MESSAGE_TYPE(type) (type & ((((uint32_t)1 << MESSAGE_TYPE_BIT) - 1))) typedef struct { uint8_t m_nil; } nil_t; +typedef struct message_array_t message_array_t; enum data_type { NIL = 0, TBOOLEAN = 1, USERDATA = 2, //light userdata NUMBER = 3, - BUFFER = 4, //need to be freed - STRING = 5, //need to be freed - TERROR = 6 // + INTEGER = 4, //support after lua53 + BUFFER = 5, //need to be freed + STRING = 6, //need to be freed + BINARY = 7, //need to be freed + TPACK = 8, //need to be freed + SHARED = 9, //need to be freed + ARRAY = 10,//need to be freed + TERROR = 11 // }; typedef union data_t { - bool m_bool; - void *m_userdata; - double m_number; - buffer_t m_buffer; - char *m_string; - int32_t m_error; + bool m_bool; + void *m_userdata; + double m_number; + int64_t m_integer; + buffer_t m_buffer; + char *m_string; + binary_t m_binary; + tpack_t m_tpack; + shared_t *m_shared; + message_array_t *m_array; + int32_t m_error; } data_t; enum message_type { @@ -39,14 +53,22 @@ enum message_type { LUA_CTX_INIT, LUA_CTX_WAIT, LUA_CTX_WAKEUP, + LUA_CTX_WAIT_CANCEL, + LUA_CTX_RUN, RESPONSE_TCP_LISTEN, RESPONSE_TCP_ACCEPT, RESPONSE_TCP_CONNECT, RESPONSE_TCP_READ, RESPONSE_TCP_WRITE, RESPONSE_TCP_CLOSING, + RESPONSE_UDP_OPEN, + RESPONSE_UDP_READ, + RESPONSE_UDP_WRITE, + RESPONSE_UDP_CLOSING, RESPONSE_HANDLE_CLOSE, - RESPONSE_TIMEOUT + RESPONSE_TIMEOUT, + //////////////////////////////////////// + LOG_MESSAGE }; #define message_raw_type(msg) (MESSAGE_TYPE((msg).m_type)) @@ -54,8 +76,13 @@ enum message_type { #define message_bool(msg) ((msg).m_data.m_bool) #define message_userdata(msg) ((msg).m_data.m_userdata) #define message_number(msg) ((msg).m_data.m_number) +#define message_integer(msg) ((msg).m_data.m_integer) #define message_buffer(msg) ((msg).m_data.m_buffer) #define message_string(msg) ((msg).m_data.m_string) +#define message_binary(msg) ((msg).m_data.m_binary) +#define message_tpack(msg) ((msg).m_data.m_tpack) +#define message_shared(msg) ((msg).m_data.m_shared) +#define message_array(msg) ((msg).m_data.m_array) #define message_error(msg) ((msg).m_data.m_error) #define message_data_type(msg) (DATA_TYPE((msg).m_type)) @@ -63,23 +90,47 @@ enum message_type { #define message_is_bool(msg) (TBOOLEAN == message_data_type(msg)) #define message_is_userdata(msg) (USERDATA == message_data_type(msg)) #define message_is_number(msg) (NUMBER == message_data_type(msg)) +#define message_is_integer(msg) (INTEGER == message_data_type(msg)) #define message_is_buffer(msg) (BUFFER == message_data_type(msg)) #define message_is_string(msg) (STRING == message_data_type(msg)) +#define message_is_binary(msg) (BINARY == message_data_type(msg)) +#define message_is_tpack(msg) (TPACK == message_data_type(msg)) +#define message_is_shared(msg) (SHARED == message_data_type(msg)) +#define message_is_array(msg) (ARRAY == message_data_type(msg)) #define message_is_error(msg) (TERROR == message_data_type(msg)) #define message_is_pure_error(msg) (TERROR == message_data_type(msg) && message_error(msg) != 0) -#define message_buffer_grab(msg) if (message_is_buffer(msg)) { buffer_grab(msg.m_data.m_buffer); } -#define message_buffer_release(msg) if (message_is_buffer(msg)) { buffer_release(msg.m_data.m_buffer); } #define message_clean(msg) do { \ if (message_is_buffer(msg)) { \ buffer_release(msg.m_data.m_buffer); \ - } else if (message_is_string(msg)&&(msg).m_data.m_string) { \ - nl_free((msg).m_data.m_string); \ - msg.m_data.m_string = NULL; \ + } else if (message_is_string(msg)) { \ + if ((msg).m_data.m_string) { \ + nl_free((msg).m_data.m_string); \ + (msg).m_data.m_string = NULL; \ + } \ + } else if (message_is_binary(msg)) { \ + if ((msg).m_data.m_binary.m_data) { \ + sdsfree((msg).m_data.m_binary.m_data); \ + (msg).m_data.m_binary.m_data = NULL; \ + } \ + } else if (message_is_tpack(msg)) { \ + if ((msg).m_data.m_tpack.m_data) { \ + sdsfree((msg).m_data.m_tpack.m_data); \ + (msg).m_data.m_tpack.m_data = NULL; \ + } \ + } else if (message_is_shared(msg)) { \ + if ((msg).m_data.m_shared) { \ + (msg).m_data.m_shared->release(); \ + (msg).m_data.m_shared = NULL; \ + } \ + } else if (message_is_array(msg)) { \ + if ((msg).m_data.m_array) { \ + message_array_release((msg).m_data.m_array); \ + (msg).m_data.m_array = NULL; \ + } \ } \ } while (0) - class message_t { public: message_t() @@ -105,6 +156,10 @@ class message_t { message_t(uint32_t source, int32_t session, uint32_t msg_type, double number) : m_source(source), m_session(session), m_type(MAKE_MESSAGE_TYPE(msg_type, NUMBER)) { m_data.m_number = number; } + + message_t(uint32_t source, int32_t session, uint32_t msg_type, int64_t integer) + : m_source(source), m_session(session), + m_type(MAKE_MESSAGE_TYPE(msg_type, INTEGER)) { m_data.m_integer = integer; } message_t(uint32_t source, int32_t session, uint32_t msg_type, char *string) : m_source(source), m_session(session), @@ -117,6 +172,22 @@ class message_t { message_t(uint32_t source, int32_t session, uint32_t msg_type, nl_err_code err) : m_source(source), m_session(session), m_type(MAKE_MESSAGE_TYPE(msg_type, TERROR)) { m_data.m_error = err; } + + message_t(uint32_t source, int32_t session, uint32_t msg_type, tpack_t tpack) + : m_source(source), m_session(session), + m_type(MAKE_MESSAGE_TYPE(msg_type, TPACK)) { m_data.m_tpack = tpack; } + + message_t(uint32_t source, int32_t session, uint32_t msg_type, binary_t binary) + : m_source(source), m_session(session), + m_type(MAKE_MESSAGE_TYPE(msg_type, BINARY)) { m_data.m_binary = binary; } + + message_t(uint32_t source, int32_t session, uint32_t msg_type, shared_t *shared) + : m_source(source), m_session(session), + m_type(MAKE_MESSAGE_TYPE(msg_type, SHARED)) { m_data.m_shared = shared; } + + message_t(uint32_t source, int32_t session, uint32_t msg_type, message_array_t *array) + : m_source(source), m_session(session), + m_type(MAKE_MESSAGE_TYPE(msg_type, ARRAY)) { m_data.m_array = array; } public: uint32_t m_source; int32_t m_session; @@ -124,4 +195,14 @@ class message_t { data_t m_data; }; +///////////////////////////////////////////////////////////////////////////////////////////////////////// +//message array api +struct message_array_t { + uint32_t m_count; + message_t m_array[1]; +}; + +extern message_array_t* message_array_create(uint32_t count); +extern void message_array_release(message_array_t* array); + #endif diff --git a/src/network.cpp b/src/network.cpp index 4c76e6c..054ee64 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -3,6 +3,7 @@ #include "buffer.h" #include "message.h" #include "uv_tcp_handle.h" +#include "uv_udp_handle.h" #include "uv_timer_handle.h" #include "network.h" #include "node_lua.h" @@ -26,12 +27,15 @@ network_t::network_t() : m_exiting(0) assert(rc == 0); rc = uv_poll_start(&m_request_handle, UV_READABLE, on_request_polled_in); assert(rc == 0); + //rc = uv_pipe_init(m_uv_loop, &m_remoter_handle, 1); + //assert(rc == 0); + memset(&m_shared_read_buffer, 0, sizeof(m_shared_read_buffer)); } network_t::~network_t() { uv_loop_delete(m_uv_loop); - uv_tcp_socket_handle_t::free_shared_read_buffer(); + free_shared_read_buffer(); } void network_t::start() @@ -132,7 +136,22 @@ void network_t::request_tcp_read(request_tcp_read_t& request) void network_t::request_tcp_write(request_tcp_write_t& request) { - request.m_socket_handle->write(request); + uv_tcp_socket_handle_t::write(request); +} + +void network_t::request_udp_open(request_udp_open_t& request) +{ + (new uv_udp_handle_t(m_uv_loop, request.m_source))->open(request); +} + +void network_t::request_udp_write(request_udp_write_t& request) +{ + uv_udp_handle_t::write(request); +} + +void network_t::request_udp_read(request_udp_read_t& request) +{ + request.m_socket_handle->read(request); } void network_t::request_handle_option(request_handle_option_t& request) @@ -161,7 +180,7 @@ bool network_t::recv_request() int32_t need_read = 0, readed = 0; int32_t processed = 0, nbytes = 0; request_t request; - for (;;) { //to be fixed : Żȡ + for (;;) { if (need_read == 0) { nbytes = ::recv(m_request_r_fd, (char*)(&request.m_length), sizeof(request.m_length), 0); if (nbytes > 0) { @@ -215,6 +234,15 @@ void network_t::process_request(request_t& request) case REQUEST_TCP_READ: request_tcp_read(request.m_tcp_read); break; + case REQUEST_UDP_OPEN: + request_udp_open(request.m_udp_open); + break; + case REQUEST_UDP_WRITE: + request_udp_write(request.m_udp_write); + break; + case REQUEST_UDP_READ: + request_udp_read(request.m_udp_read); + break; case REQUEST_HANDLE_OPTION: request_handle_option(request.m_handle_option); break; @@ -315,6 +343,46 @@ int network_t::make_socketpair( uv_os_sock_t *r_, uv_os_sock_t *w_ ) #endif } +#ifdef CC_MSVC +int network_t::make_tcp_socket(uv_os_sock_t *sock, bool ipv6, bool reuseport) +{ + *sock = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_IP); + if (*sock != INVALID_SOCKET) { + if (reuseport) { + DWORD yes = 1; + if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof yes) == SOCKET_ERROR) { + close_socket(*sock); + *sock = INVALID_SOCKET; + return -1; + } + } + return 0; + } + return -1; +} +#else +extern "C" { + extern int uv__socket(int domain, int type, int protocol); +} + +int network_t::make_tcp_socket(uv_os_sock_t *sock, bool ipv6, bool reuseport) +{ + *sock = uv__socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_IP); + if (*sock >= 0) { + if (reuseport) { + int yes = 1; + if (setsockopt(*sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) != 0) { + close_socket(*sock); + *sock = -1; + return -1; + } + } + return 0; + } + return -1; +} +#endif + int network_t::set_noneblocking( uv_os_sock_t sock ) { #if defined CC_MSVC @@ -364,4 +432,3 @@ int network_t::close_socketpair( uv_os_sock_t *r, uv_os_sock_t *w ) #endif return 0; } - diff --git a/src/network.h b/src/network.h index 5b97423..e3380cf 100644 --- a/src/network.h +++ b/src/network.h @@ -1,8 +1,12 @@ #ifndef NETWORK_H_ #define NETWORK_H_ +#include #include "singleton.h" #include "request.h" +#define SHARED_READ_BUFFER_SIZE (64 * 1024) +typedef std::map shared_write_map_t; + class message_queue_t; class network_t : public singleton_t { @@ -15,6 +19,9 @@ class network_t : public singleton_t { uv_os_sock_t m_request_r_fd; uv_os_sock_t m_request_w_fd; uv_poll_t m_request_handle; + uv_pipe_t m_remoter_handle; + uv_buf_t m_shared_read_buffer; /* tcp and udp shared read buffer in network thread */ + shared_write_map_t m_shared_write_sockets; /* tcp and udp shared write socket in network thread */ atomic_t m_exiting; public: uv_err_code last_error() const { return uv_last_error(m_uv_loop).code; } @@ -33,8 +40,42 @@ class network_t : public singleton_t { public: static int make_socketpair(uv_os_sock_t *r, uv_os_sock_t *w); static int close_socketpair(uv_os_sock_t *r, uv_os_sock_t *w); + static int make_tcp_socket(uv_os_sock_t *sock, bool ipv6, bool reuseport); static int set_noneblocking(uv_os_sock_t sock); static int close_socket(uv_os_sock_t sock); +public: + FORCE_INLINE void free_shared_read_buffer() { + if (m_shared_read_buffer.base) { + nl_free(m_shared_read_buffer.base); + m_shared_read_buffer.base = NULL; + m_shared_read_buffer.len = 0; + } + } + FORCE_INLINE uv_buf_t get_shared_read_buffer() const { + return m_shared_read_buffer; + } + FORCE_INLINE uv_buf_t make_shared_read_buffer() { + if (m_shared_read_buffer.base) { + return m_shared_read_buffer; + } + m_shared_read_buffer.base = (char*)nl_malloc(SHARED_READ_BUFFER_SIZE); + assert(m_shared_read_buffer.base != NULL); + m_shared_read_buffer.len = SHARED_READ_BUFFER_SIZE; + return m_shared_read_buffer; + } + FORCE_INLINE void put_shared_write_socket(int64_t fd, uv_handle_base_t* socket) { + m_shared_write_sockets[fd] = socket; + } + FORCE_INLINE uv_handle_base_t* get_shared_write_socket(int64_t fd) const { + shared_write_map_t::const_iterator it = m_shared_write_sockets.find(fd); + if (it != m_shared_write_sockets.end()) { + return it->second; + } + return NULL; + } + FORCE_INLINE void pop_shared_write_socket(int64_t fd) { + m_shared_write_sockets.erase(fd); + } private: void request_exit(request_exit_t& request); void request_tcp_listen(request_tcp_listen_t& request); @@ -44,6 +85,9 @@ class network_t : public singleton_t { void request_tcp_connects(request_tcp_connects_t& request); void request_tcp_write(request_tcp_write_t& request); void request_tcp_read(request_tcp_read_t& request); + void request_udp_open(request_udp_open_t& request); + void request_udp_write(request_udp_write_t& request); + void request_udp_read(request_udp_read_t& request); void request_handle_option(request_handle_option_t& request); void request_handle_close(request_handle_close_t& request); void request_timer_start(request_timer_start_t& request); diff --git a/src/nlmalloc.h b/src/nlmalloc.h new file mode 100644 index 0000000..1c4c347 --- /dev/null +++ b/src/nlmalloc.h @@ -0,0 +1,12 @@ +#ifndef NLMALLOC_H_ +#define NLMALLOC_H_ +#include +#include + +#define nl_malloc malloc +#define nl_free free +#define nl_calloc(size) calloc(1, size) +#define nl_realloc realloc +#define nl_strdup strdup + +#endif diff --git a/src/node_lua.cpp b/src/node_lua.cpp index e817bcf..533ca0c 100644 --- a/src/node_lua.cpp +++ b/src/node_lua.cpp @@ -1,6 +1,7 @@ #include "common.h" #include "context.h" #include "network.h" +#include "context_log.h" #include "context_lua.h" #include "context_mgr.h" #include "worker_mgr.h" @@ -28,7 +29,7 @@ void node_lua_t::init_once() m_inited = true; } -node_lua_t::node_lua_t(int argc, char* argv[], char* env[]) +node_lua_t::node_lua_t(int argc, char* argv[], char* env[]) : m_logger(NULL) { node_lua_t::init_once(); m_network = new network_t(); @@ -36,10 +37,24 @@ node_lua_t::node_lua_t(int argc, char* argv[], char* env[]) m_worker_mgr = new worker_mgr_t(); m_network->start(); m_worker_mgr->start(); - context_create(argc, argv, env); + + /* start basic context */ + m_ctx_mgr->set_handle_index(MAX_CTX_SIZE); + uint32_t logger_handle = context_create(0, 0, NULL, NULL); + if (logger_handle > 0) { + m_logger = m_ctx_mgr->grab_context(logger_handle); + m_ctx_mgr->set_handle_index(1); + context_create(0, argc, argv, env); + } + m_worker_mgr->wait(); m_network->stop(); m_network->wait(); + if (m_logger) { + m_logger->release(); + m_logger = NULL; + } + context_lua_t::unload(); } node_lua_t::~node_lua_t() @@ -53,14 +68,12 @@ bool node_lua_t::context_send(context_t* ctx, message_t& msg) { if (ctx->get_handle() != 0 && (ctx->is_inited() || ctx->get_handle() == msg.m_source)) { bool processing; - message_buffer_grab(msg); if (ctx->push_message(msg, processing)) { if (!processing) { m_worker_mgr->push_context(ctx); } return true; } - message_buffer_release(msg); } return false; } diff --git a/src/node_lua.h b/src/node_lua.h index 17a2764..56e0089 100644 --- a/src/node_lua.h +++ b/src/node_lua.h @@ -5,6 +5,7 @@ #include "context_mgr.h" #include "worker_mgr.h" #include "context.h" +#include "lbinary.h" class message_t; class network_t; @@ -16,7 +17,7 @@ class node_lua_t : public singleton_t { ~node_lua_t(); /* context send by ctx */ bool context_send(context_t* ctx, message_t& msg); - template < class type> + template < class type > FORCE_INLINE bool context_send(context_t* ctx, uint32_t source, int session, int msg_type, type data) { message_t msg(source, session, msg_type, data); @@ -52,12 +53,57 @@ class node_lua_t : public singleton_t { message_clean(msg); return false; } + + /* specific context send interface */ + FORCE_INLINE bool context_send_binary_safe(uint32_t handle, uint32_t source, int session, int msg_type, const char *data, int32_t length) + { + binary_t binary = { data ? sdsnewlen(data, length) : NULL }; + message_t msg(source, session, msg_type, binary); + if (context_send(handle, msg)) + return true; + message_clean(msg); + return false; + } + + FORCE_INLINE bool context_send_binary_safe(context_t* ctx, uint32_t source, int session, int msg_type, const char *data, int32_t length) + { + binary_t binary = { data ? sdsnewlen(data, length) : NULL }; + message_t msg(source, session, msg_type, binary); + if (context_send(ctx, msg)) + return true; + message_clean(msg); + return false; + } + FORCE_INLINE bool context_send_buffer_safe(uint32_t handle, uint32_t source, int session, int msg_type, buffer_t& buffer) + { + buffer_grab(buffer); + message_t msg(source, session, msg_type, buffer); + bool ret = context_send(handle, msg); + if (!ret) { + buffer_release(buffer); + } + return ret; + } + + FORCE_INLINE bool context_send_buffer_safe(context_t* ctx, uint32_t source, int session, int msg_type, buffer_t& buffer) + { + buffer_grab(buffer); + message_t msg(source, session, msg_type, buffer); + bool ret = context_send(ctx, msg); + if (!ret) { + buffer_release(buffer); + } + return ret; + } + FORCE_INLINE bool context_send_buffer_release(uint32_t handle, uint32_t source, int session, int msg_type, buffer_t& buffer) { message_t msg(source, session, msg_type, buffer); bool ret = context_send(handle, msg); - buffer_release(buffer); + if (!ret) { + buffer_release(buffer); + } return ret; } @@ -65,15 +111,37 @@ class node_lua_t : public singleton_t { { message_t msg(source, session, msg_type, buffer); bool ret = context_send(ctx, msg); - buffer_release(buffer); + if (!ret) { + buffer_release(buffer); + } + return ret; + } + + FORCE_INLINE bool context_send_array_release(uint32_t handle, uint32_t source, int session, int msg_type, message_array_t* array) + { + message_t msg(source, session, msg_type, array); + bool ret = context_send(handle, msg); + if (!ret) { + message_clean(msg); + } + return ret; + } + + FORCE_INLINE bool context_send_array_release(context_t* ctx, uint32_t source, int session, int msg_type, message_array_t* array) + { + message_t msg(source, session, msg_type, array); + bool ret = context_send(ctx, msg); + if (!ret) { + message_clean(msg); + } return ret; } template < class type > - uint32_t context_create(int32_t argc, char* argv[], char* env[]) { + uint32_t context_create(uint32_t parent, int32_t argc, char* argv[], char* env[]) { uint32_t handle = 0; type *ctx = new type(); - if (m_ctx_mgr->register_context(ctx)) { + if (m_ctx_mgr->register_context(ctx, parent)) { if (ctx->init(argc, argv, env)) { ctx->set_inited(true); handle = ctx->get_handle(); @@ -85,9 +153,7 @@ class node_lua_t : public singleton_t { m_ctx_mgr->retire_context(ctx); } ctx->release(); - if (m_ctx_mgr->get_context_count() == 0) { - m_worker_mgr->stop(); - } + context_check_alive(parent); return 0; } @@ -113,7 +179,7 @@ class node_lua_t : public singleton_t { uint32_t handle = ctx->get_handle(); if (handle == 0) return; - char buffer[512] = { '\0' }; + char buffer[1024] = { '\0' }; int32_t used = sprintf(buffer, "killed by context:0x%08x", src_handle); if (reason) { strcat(buffer + used, ", "); @@ -127,15 +193,68 @@ class node_lua_t : public singleton_t { ctx->deinit(buffer); /* ctx->m_handle is valid in this function. */ ctx->set_inited(false); m_ctx_mgr->retire_context(ctx); - if (m_ctx_mgr->get_context_count() == 0) { + context_check_alive(src_handle); + } + + void context_check_alive(uint32_t src_handle) + { + uint32_t ctx_size = m_ctx_mgr->get_context_count(); + if (ctx_size == 1) { + context_destroy(m_logger->get_handle(), src_handle, NULL); + return; + } + if (ctx_size == 0) { m_worker_mgr->stop(); + return; + } + } + + bool log_binary_release(uint32_t src_handle, binary_t binary) { + if (m_logger && context_send(m_logger, src_handle, 0, LOG_MESSAGE, binary)) { + return true; + } + sdsfree(binary.m_data); + return false; + } + + bool log_fmt(uint32_t src_handle, const char* fmt, ...) { + if (m_logger && fmt) { + char *buf = NULL; + size_t buflen = 64; + va_list argp; + va_list copy; + va_start(argp, fmt); + while (1) { + buf = (char *)nl_malloc(buflen); + if (buf == NULL) break; + buf[buflen - 2] = '\0'; + va_copy(copy, argp); + vsnprintf(buf, buflen, fmt, copy); + if (buf[buflen - 2] != '\0') { + nl_free(buf); + buflen *= 2; + continue; + } + break; + } + va_end(argp); + if (buf && context_send(m_logger, src_handle, 0, LOG_MESSAGE, buf)) { + return true; + } + nl_free(buf); } + return false; + } + + FORCE_INLINE uint32_t is_handle_illegal(uint32_t handle) const { + return handle == m_logger->get_handle(); } private: network_t *m_network; context_mgr_t *m_ctx_mgr; worker_mgr_t *m_worker_mgr; + context_t *m_logger; public: static int32_t m_cpu_count; diff --git a/src/ref_sessions_mgr.cpp b/src/ref_sessions_mgr.cpp index 9f93817..c53b5ea 100644 --- a/src/ref_sessions_mgr.cpp +++ b/src/ref_sessions_mgr.cpp @@ -153,7 +153,7 @@ void ref_sessions_mgr_t::put_cached_sessions(ref_sessions_t* ref_sessions) void ref_sessions_mgr_t::clear_cached_sessions() { - for (int32_t i = 0; i < m_cached_sessions.size(); ++i) { + for (size_t i = 0; i < m_cached_sessions.size(); ++i) { delete (m_cached_sessions[i]); } m_cached_sessions.clear(); diff --git a/src/ref_sessions_mgr.h b/src/ref_sessions_mgr.h index 45c98c1..d698c3c 100644 --- a/src/ref_sessions_mgr.h +++ b/src/ref_sessions_mgr.h @@ -28,6 +28,7 @@ class ref_sessions_mgr_t void wakeup_all(uint32_t id, context_lua_t* lctx, message_t& message, bool free_callback = false); private: + friend class context_lua_t; typedef std::map ref_sessions_map_t; typedef std::vector ref_sessions_vec_t; ref_sessions_map_t m_sessions_map; diff --git a/src/request.h b/src/request.h index d18c7ea..f37be24 100644 --- a/src/request.h +++ b/src/request.h @@ -1,13 +1,13 @@ #ifndef REQUEST_H_ #define REQUEST_H_ -#include "uv.h" -#include "lua.hpp" +#include "common.h" #include "buffer.h" class uv_handle_base_t; class uv_tcp_socket_handle_t; class uv_tcp_listen_handle_t; +class uv_udp_handle_t; class uv_timer_handle_t; #define REQUEST_SIZE_MAX 255 @@ -25,6 +25,9 @@ enum request_type { REQUEST_TCP_CONNECTS, REQUEST_TCP_READ, REQUEST_TCP_WRITE, + REQUEST_UDP_OPEN, + REQUEST_UDP_READ, + REQUEST_UDP_WRITE, REQUEST_HANDLE_OPTION, REQUEST_HANDLE_CLOSE, REQUEST_TIMER_START @@ -39,9 +42,9 @@ struct request_exit_t { struct request_tcp_listen_t { uint32_t m_source; uint32_t m_session; - uint16_t m_backlog; uint16_t m_port; bool m_ipv6; + bool m_reuse; REQUEST_SPARE_REGION }; @@ -81,13 +84,49 @@ struct request_tcp_read_t { }; struct request_tcp_write_t { - uv_tcp_socket_handle_t *m_socket_handle; + union { + uv_tcp_socket_handle_t *m_socket_handle; + uint64_t m_socket_fd; + }; + union { + const char* m_string; + buffer_t m_buffer; + }; + uint32_t m_length; /* judge it's string or buffer */ + uint32_t m_source; /* redundant if m_shared_write is true */ uint32_t m_session; - uint32_t m_length; + bool m_shared_write; /* whether m_socket_fd is valid */ + REQUEST_SPARE_REGION +}; + +struct request_udp_open_t { + uint32_t m_source; + uint32_t m_session; + uint16_t m_port; + bool m_ipv6; + REQUEST_SPARE_REGION +}; + +struct request_udp_read_t { + uv_udp_handle_t *m_socket_handle; + REQUEST_SPARE_REGION +}; + +struct request_udp_write_t { + union { + uv_udp_handle_t *m_socket_handle; + uint64_t m_socket_fd; + }; union { const char* m_string; buffer_t m_buffer; }; + uint32_t m_length; /* judge it's string or buffer */ + uint32_t m_source; /* redundant if m_shared_write is true */ + uint32_t m_session; + uint16_t m_port; + bool m_ipv6; + bool m_shared_write; /* whether m_socket_fd is valid */ REQUEST_SPARE_REGION }; @@ -123,6 +162,9 @@ struct request_t { request_tcp_connects_t m_tcp_connects; request_tcp_read_t m_tcp_read; request_tcp_write_t m_tcp_write; + request_udp_open_t m_udp_open; + request_udp_read_t m_udp_read; + request_udp_write_t m_udp_write; request_handle_option_t m_handle_option; request_handle_close_t m_handle_close; request_timer_start_t m_timer_start; diff --git a/src/sds.cpp b/src/sds.cpp new file mode 100644 index 0000000..e51f189 --- /dev/null +++ b/src/sds.cpp @@ -0,0 +1,892 @@ +/* SDSLib, A C dynamic strings library + * + * Copyright (c) 2006-2012, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#include +#include +#include +#include +#include +#include "sds.h" +#include "nlmalloc.h" + +/* Create a new sds string with the content specified by the 'init' pointer + * and 'initlen'. + * If NULL is used for 'init' the string is initialized with zero bytes. + * + * The string is always null-termined (all the sds strings are, always) so + * even if you create an sds string with: + * + * mystring = sdsnewlen("abc",3"); + * + * You can print the string with printf() as there is an implicit \0 at the + * end of the string. However the string is binary safe and can contain + * \0 characters in the middle, as the length is stored in the sds header. */ +sds sdsnewlen(const void *init, size_t initlen) { + struct sdshdr *sh; + + if (init) { + sh = (struct sdshdr *)nl_malloc(sizeof(struct sdshdr) + initlen + 1); + } else { + sh = (struct sdshdr *)nl_calloc(sizeof(struct sdshdr) + initlen + 1); + } + if (sh == NULL) return NULL; + sh->len = initlen; + sh->free = 0; + if (initlen && init) + memcpy(sh->buf, init, initlen); + sh->buf[initlen] = '\0'; + return (char*)sh->buf; +} + +/* Create an empty (zero length) sds string. Even in this case the string + * always has an implicit null term. */ +sds sdsempty(void) { + return sdsnewlen("",0); +} + +/* Create a new sds string starting from a null termined C string. */ +sds sdsnew(const char *init) { + size_t initlen = (init == NULL) ? 0 : strlen(init); + return sdsnewlen(init, initlen); +} + +/* Create an empty sds string with capacity. Even in this case the string +* always has an implicit null term. */ +sds sdsnewempty(size_t capacity) { + struct sdshdr *sh = (struct sdshdr *)nl_calloc(sizeof(struct sdshdr) + capacity + 1); + if (sh == NULL) return NULL; + sh->len = 0; + sh->free = capacity; + return (char*)sh->buf; +} + +/* Duplicate an sds string. */ +sds sdsdup(const sds s) { + return sdsnewlen(s, sdslen(s)); +} + +/* Free an sds string. No operation is performed if 's' is NULL. */ +void sdsfree(sds s) { + if (s == NULL) return; + nl_free(s-sizeof(struct sdshdr)); +} + +/* Set the sds string length to the length as obtained with strlen(), so + * considering as content only up to the first null term character. + * + * This function is useful when the sds string is hacked manually in some + * way, like in the following example: + * + * s = sdsnew("foobar"); + * s[2] = '\0'; + * sdsupdatelen(s); + * printf("%d\n", sdslen(s)); + * + * The output will be "2", but if we comment out the call to sdsupdatelen() + * the output will be "6" as the string was modified but the logical length + * remains 6 bytes. */ +void sdsupdatelen(sds s) { + struct sdshdr *sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + int reallen = strlen(s); + sh->free += (sh->len-reallen); + sh->len = reallen; +} + +/* Modify an sds string on-place to make it empty (zero length). + * However all the existing buffer is not discarded but set as free space + * so that next append operations will not require allocations up to the + * number of bytes previously available. */ +void sdsclear(sds s) { + struct sdshdr *sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + sh->free += sh->len; + sh->len = 0; + sh->buf[0] = '\0'; +} + +/* Enlarge the free space at the end of the sds string so that the caller + * is sure that after calling this function can overwrite up to addlen + * bytes after the end of the string, plus one more byte for nul term. + * + * Note: this does not change the *length* of the sds string as returned + * by sdslen(), but only the free buffer space we have. */ +sds sdsMakeRoomFor(sds s, size_t addlen) { + struct sdshdr *sh, *newsh; + size_t free = sdsavail(s); + size_t len, newlen; + + if (free >= addlen) return s; + len = sdslen(s); + sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + newlen = (len+addlen); + if (newlen < SDS_MAX_PREALLOC) + newlen *= 2; + else + newlen += SDS_MAX_PREALLOC; + newsh = (struct sdshdr*)nl_realloc(sh, sizeof(struct sdshdr) + newlen + 1); + if (newsh == NULL) return NULL; + + newsh->free = newlen - len; + return newsh->buf; +} + +/* Reallocate the sds string so that it has no free space at the end. The + * contained string remains not altered, but next concatenation operations + * will require a reallocation. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdsRemoveFreeSpace(sds s) { + struct sdshdr *sh; + + sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + sh = (struct sdshdr*)nl_realloc(sh, sizeof(struct sdshdr) + sh->len + 1); + sh->free = 0; + return sh->buf; +} + +/* Return the total size of the allocation of the specifed sds string, + * including: + * 1) The sds header before the pointer. + * 2) The string. + * 3) The free buffer at the end if any. + * 4) The implicit null term. + */ +size_t sdsAllocSize(sds s) { + struct sdshdr *sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + + return sizeof(*sh)+sh->len+sh->free+1; +} + +/* Increment the sds length and decrements the left free space at the + * end of the string according to 'incr'. Also set the null term + * in the new end of the string. + * + * This function is used in order to fix the string length after the + * user calls sdsMakeRoomFor(), writes something after the end of + * the current string, and finally needs to set the new length. + * + * Note: it is possible to use a negative increment in order to + * right-trim the string. + * + * Usage example: + * + * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the + * following schema, to cat bytes coming from the kernel to the end of an + * sds string without copying into an intermediate buffer: + * + * oldlen = sdslen(s); + * s = sdsMakeRoomFor(s, BUFFER_SIZE); + * nread = read(fd, s+oldlen, BUFFER_SIZE); + * ... check for nread <= 0 and handle it ... + * sdsIncrLen(s, nread); + */ +void sdsIncrLen(sds s, int incr) { + struct sdshdr *sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + + assert(sh->free >= incr); + sh->len += incr; + sh->free -= incr; + assert(sh->free >= 0); + s[sh->len] = '\0'; +} + +/* Grow the sds to have the specified length. Bytes that were not part of + * the original length of the sds will be set to zero. + * + * if the specified length is smaller than the current length, no operation + * is performed. */ +sds sdsgrowzero(sds s, size_t len) { + struct sdshdr *sh = (struct sdshdr*)(s - (sizeof(struct sdshdr))); + size_t totlen, curlen = sh->len; + + if (len <= curlen) return s; + s = sdsMakeRoomFor(s,len-curlen); + if (s == NULL) return NULL; + + /* Make sure added region doesn't contain garbage */ + sh = (struct sdshdr*)(s - (sizeof(struct sdshdr))); + memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ + totlen = sh->len+sh->free; + sh->len = len; + sh->free = totlen-sh->len; + return s; +} + +/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the + * end of the specified sds string 's'. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatlen(sds s, const void *t, size_t len) { + struct sdshdr *sh; + size_t curlen = sdslen(s); + + s = sdsMakeRoomFor(s,len); + if (s == NULL) return NULL; + sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + memcpy(s+curlen, t, len); + sh->len = curlen+len; + sh->free = sh->free-len; + s[curlen+len] = '\0'; + return s; +} + +/* Append the specified null termianted C string to the sds string 's'. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscat(sds s, const char *t) { + return sdscatlen(s, t, strlen(t)); +} + +/* Append the specified sds 't' to the existing sds 's'. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatsds(sds s, const sds t) { + return sdscatlen(s, t, sdslen(t)); +} + +/* Destructively modify the sds string 's' to hold the specified binary + * safe string pointed by 't' of length 'len' bytes. */ +sds sdscpylen(sds s, const char *t, size_t len) { + struct sdshdr *sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + size_t totlen = sh->free+sh->len; + + if (totlen < len) { + s = sdsMakeRoomFor(s,len-sh->len); + if (s == NULL) return NULL; + sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + totlen = sh->free+sh->len; + } + memcpy(s, t, len); + s[len] = '\0'; + sh->len = len; + sh->free = totlen-len; + return s; +} + +/* Like sdscpylen() but 't' must be a null-termined string so that the length + * of the string is obtained with strlen(). */ +sds sdscpy(sds s, const char *t) { + return sdscpylen(s, t, strlen(t)); +} + +/* Like sdscatpritf() but gets va_list instead of being variadic. */ +sds sdscatvprintf(sds s, const char *fmt, va_list ap) { + va_list cpy; + char *buf, *t; + size_t buflen = 16; + + while(1) { + buf = (char *)nl_malloc(buflen); + if (buf == NULL) return NULL; + buf[buflen-2] = '\0'; + va_copy(cpy,ap); + vsnprintf(buf, buflen, fmt, cpy); + if (buf[buflen-2] != '\0') { + nl_free(buf); + buflen *= 2; + continue; + } + break; + } + t = sdscat(s, buf); + nl_free(buf); + return t; +} + +/* Append to the sds string 's' a string obtained using printf-alike format + * specifier. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. + * + * Example: + * + * s = sdsempty("Sum is: "); + * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). + * + * Often you need to create a string from scratch with the printf-alike + * format. When this is the need, just use sdsempty() as the target string: + * + * s = sdscatprintf(sdsempty(), "... your format ...", args); + */ +sds sdscatprintf(sds s, const char *fmt, ...) { + va_list ap; + char *t; + va_start(ap, fmt); + t = sdscatvprintf(s,fmt,ap); + va_end(ap); + return t; +} + +/* Remove the part of the string from left and from right composed just of + * contiguous characters found in 'cset', that is a null terminted C string. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. + * + * Example: + * + * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); + * s = sdstrim(s,"A. :"); + * printf("%s\n", s); + * + * Output will be just "Hello World". + */ +sds sdstrim(sds s, const char *cset) { + struct sdshdr *sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + char *start, *end, *sp, *ep; + size_t len; + + sp = start = s; + ep = end = s+sdslen(s)-1; + while(sp <= end && strchr(cset, *sp)) sp++; + while(ep > start && strchr(cset, *ep)) ep--; + len = (sp > ep) ? 0 : ((ep-sp)+1); + if (sh->buf != sp) memmove(sh->buf, sp, len); + sh->buf[len] = '\0'; + sh->free = sh->free+(sh->len-len); + sh->len = len; + return s; +} + +/* Turn the string into a smaller (or equal) string containing only the + * substring specified by the 'start' and 'end' indexes. + * + * start and end can be negative, where -1 means the last character of the + * string, -2 the penultimate character, and so forth. + * + * The interval is inclusive, so the start and end characters will be part + * of the resulting string. + * + * The string is modified in-place. + * + * Example: + * + * s = sdsnew("Hello World"); + * sdstrim(s,1,-1); => "ello Worl" + */ +void sdsrange(sds s, int start, int end) { + struct sdshdr *sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + size_t newlen, len = sdslen(s); + + if (len == 0) return; + if (start < 0) { + start = len+start; + if (start < 0) start = 0; + } + if (end < 0) { + end = len+end; + if (end < 0) end = 0; + } + newlen = (start > end) ? 0 : (end-start)+1; + if (newlen != 0) { + if (start >= (signed)len) { + newlen = 0; + } else if (end >= (signed)len) { + end = len-1; + newlen = (start > end) ? 0 : (end-start)+1; + } + } else { + start = 0; + } + if (start && newlen) memmove(sh->buf, sh->buf+start, newlen); + sh->buf[newlen] = 0; + sh->free = sh->free+(sh->len-newlen); + sh->len = newlen; +} + +/* Apply tolower() to every character of the sds string 's'. */ +void sdstolower(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = tolower(s[j]); +} + +/* Apply toupper() to every character of the sds string 's'. */ +void sdstoupper(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = toupper(s[j]); +} + +/* Compare two sds strings s1 and s2 with memcmp(). + * + * Return value: + * + * 1 if s1 > s2. + * -1 if s1 < s2. + * 0 if s1 and s2 are exactly the same binary string. + * + * If two strings share exactly the same prefix, but one of the two has + * additional characters, the longer string is considered to be greater than + * the smaller one. */ +int sdscmp(const sds s1, const sds s2) { + size_t l1, l2, minlen; + int cmp; + + l1 = sdslen(s1); + l2 = sdslen(s2); + minlen = (l1 < l2) ? l1 : l2; + cmp = memcmp(s1,s2,minlen); + if (cmp == 0) return l1-l2; + return cmp; +} + +/* Split 's' with separator in 'sep'. An array + * of sds strings is returned. *count will be set + * by reference to the number of tokens returned. + * + * On out of memory, zero length string, zero length + * separator, NULL is returned. + * + * Note that 'sep' is able to split a string using + * a multi-character separator. For example + * sdssplit("foo_-_bar","_-_"); will return two + * elements "foo" and "bar". + * + * This version of the function is binary-safe but + * requires length arguments. sdssplit() is just the + * same function but for zero-terminated strings. + */ +sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { + int elements = 0, slots = 5, start = 0, j; + sds *tokens; + + if (seplen < 1 || len < 0) return NULL; + + tokens = (sds *)nl_malloc(sizeof(sds)*slots); + if (tokens == NULL) return NULL; + + if (len == 0) { + *count = 0; + return tokens; + } + for (j = 0; j < (len-(seplen-1)); j++) { + /* make sure there is room for the next element and the final one */ + if (slots < elements+2) { + sds *newtokens; + + slots *= 2; + newtokens = (sds *)nl_realloc(tokens, sizeof(sds)*slots); + if (newtokens == NULL) goto cleanup; + tokens = newtokens; + } + /* search the separator */ + if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { + tokens[elements] = sdsnewlen(s+start,j-start); + if (tokens[elements] == NULL) goto cleanup; + elements++; + start = j+seplen; + j = j+seplen-1; /* skip the separator */ + } + } + /* Add the final element. We are sure there is room in the tokens array. */ + tokens[elements] = sdsnewlen(s+start,len-start); + if (tokens[elements] == NULL) goto cleanup; + elements++; + *count = elements; + return tokens; + +cleanup: + { + int i; + for (i = 0; i < elements; i++) sdsfree(tokens[i]); + nl_free(tokens); + *count = 0; + return NULL; + } +} + +/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ +void sdsfreesplitres(sds *tokens, int count) { + if (!tokens) return; + while(count--) + sdsfree(tokens[count]); + nl_free(tokens); +} + +/* Create an sds string from a long long value. It is much faster than: + * + * sdscatprintf(sdsempty(),"%lld\n", value); + */ +sds sdsfromlonglong(long long value) { + char buf[32], *p; + unsigned long long v; + + v = (value < 0) ? -value : value; + p = buf+31; /* point to the last character */ + do { + *p-- = '0'+(v%10); + v /= 10; + } while(v); + if (value < 0) *p-- = '-'; + p++; + return sdsnewlen(p,32-(p-buf)); +} + +/* Append to the sds string "s" an escaped string representation where + * all the non-printable characters (tested with isprint()) are turned into + * escapes in the form "\n\r\a...." or "\x". + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatrepr(sds s, const char *p, size_t len) { + s = sdscatlen(s,"\"",1); + while(len--) { + switch(*p) { + case '\\': + case '"': + s = sdscatprintf(s,"\\%c",*p); + break; + case '\n': s = sdscatlen(s,"\\n",2); break; + case '\r': s = sdscatlen(s,"\\r",2); break; + case '\t': s = sdscatlen(s,"\\t",2); break; + case '\a': s = sdscatlen(s,"\\a",2); break; + case '\b': s = sdscatlen(s,"\\b",2); break; + default: + if (isprint(*p)) + s = sdscatprintf(s,"%c",*p); + else + s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); + break; + } + p++; + } + return sdscatlen(s,"\"",1); +} + +/* Helper function for sdssplitargs() that returns non zero if 'c' + * is a valid hex digit. */ +int is_hex_digit(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); +} + +/* Helper function for sdssplitargs() that converts an hex digit into an + * integer from 0 to 15 */ +int hex_digit_to_int(char c) { + switch(c) { + 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; + default: return 0; + } +} + +/* Split a line into arguments, where every argument can be in the + * following programming-language REPL-alike form: + * + * foo bar "newline are supported\n" and "\xff\x00otherstuff" + * + * The number of arguments is stored into *argc, and an array + * of sds is returned. + * + * The caller should free the resulting array of sds strings with + * sdsfreesplitres(). + * + * Note that sdscatrepr() is able to convert back a string into + * a quoted string in the same format sdssplitargs() is able to parse. + * + * The function returns the allocated tokens on success, even when the + * input string is empty, or NULL if the input contains unbalanced + * quotes or closed quotes followed by non space characters + * as in: "foo"bar or "foo' + */ +sds *sdssplitargs(const char *line, int *argc) { + const char *p = line; + char *current = NULL; + char **vector = NULL; + + *argc = 0; + while(1) { + /* skip blanks */ + while(*p && isspace(*p)) p++; + if (*p) { + /* get a token */ + int inq=0; /* set to 1 if we are in "quotes" */ + int insq=0; /* set to 1 if we are in 'single quotes' */ + int done=0; + + if (current == NULL) current = sdsempty(); + while(!done) { + if (inq) { + if (*p == '\\' && *(p+1) == 'x' && + is_hex_digit(*(p+2)) && + is_hex_digit(*(p+3))) + { + unsigned char byte; + + byte = (hex_digit_to_int(*(p+2))*16)+ + hex_digit_to_int(*(p+3)); + current = sdscatlen(current,(char*)&byte,1); + p += 3; + } else if (*p == '\\' && *(p+1)) { + char c; + + p++; + switch(*p) { + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'b': c = '\b'; break; + case 'a': c = '\a'; break; + default: c = *p; break; + } + current = sdscatlen(current,&c,1); + } else if (*p == '"') { + /* closing quote must be followed by a space or + * nothing at all. */ + if (*(p+1) && !isspace(*(p+1))) goto err; + done=1; + } else if (!*p) { + /* unterminated quotes */ + goto err; + } else { + current = sdscatlen(current,p,1); + } + } else if (insq) { + if (*p == '\\' && *(p+1) == '\'') { + p++; + current = sdscatlen(current,"'",1); + } else if (*p == '\'') { + /* closing quote must be followed by a space or + * nothing at all. */ + if (*(p+1) && !isspace(*(p+1))) goto err; + done=1; + } else if (!*p) { + /* unterminated quotes */ + goto err; + } else { + current = sdscatlen(current,p,1); + } + } else { + switch(*p) { + case ' ': + case '\n': + case '\r': + case '\t': + case '\0': + done=1; + break; + case '"': + inq=1; + break; + case '\'': + insq=1; + break; + default: + current = sdscatlen(current,p,1); + break; + } + } + if (*p) p++; + } + /* add the token to the vector */ + vector = (char **)nl_realloc(vector, ((*argc) + 1)*sizeof(char*)); + vector[*argc] = current; + (*argc)++; + current = NULL; + } else { + /* Even on empty input string return something not NULL. */ + if (vector == NULL) vector = (char **)nl_malloc(sizeof(void*)); + return vector; + } + } + +err: + while((*argc)--) + sdsfree(vector[*argc]); + nl_free(vector); + if (current) sdsfree(current); + *argc = 0; + return NULL; +} + +/* Modify the string substituting all the occurrences of the set of + * characters specified in the 'from' string to the corresponding character + * in the 'to' array. + * + * For instance: sdsmapchars(mystring, "ho", "01", 2) + * will have the effect of turning the string "hello" into "0ell1". + * + * The function returns the sds string pointer, that is always the same + * as the input pointer since no resize is needed. */ +sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { + size_t j, i, l = sdslen(s); + + for (j = 0; j < l; j++) { + for (i = 0; i < setlen; i++) { + if (s[j] == from[i]) { + s[j] = to[i]; + break; + } + } + } + return s; +} + +/* Join an array of C strings using the specified separator (also a C string). + * Returns the result as an sds string. */ +sds sdsjoin(char **argv, int argc, char *sep) { + sds join = sdsempty(); + int j; + + for (j = 0; j < argc; j++) { + join = sdscat(join, argv[j]); + if (j != argc-1) join = sdscat(join,sep); + } + return join; +} + +#ifdef SDS_TEST_MAIN +#include +#include "testhelp.h" + +int main(void) { + { + struct sdshdr *sh; + sds x = sdsnew("foo"), y; + + test_cond("Create a string and obtain the length", + sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) + + sdsfree(x); + x = sdsnewlen("foo",2); + test_cond("Create a string with specified length", + sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) + + x = sdscat(x,"bar"); + test_cond("Strings concatenation", + sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); + + x = sdscpy(x,"a"); + test_cond("sdscpy() against an originally longer string", + sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) + + x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); + test_cond("sdscpy() against an originally shorter string", + sdslen(x) == 33 && + memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) + + sdsfree(x); + x = sdscatprintf(sdsempty(),"%d",123); + test_cond("sdscatprintf() seems working in the base case", + sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) + + sdsfree(x); + x = sdstrim(sdsnew("xxciaoyyy"),"xy"); + test_cond("sdstrim() correctly trims characters", + sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) + + y = sdsrange(sdsdup(x),1,1); + test_cond("sdsrange(...,1,1)", + sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),1,-1); + test_cond("sdsrange(...,1,-1)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),-2,-1); + test_cond("sdsrange(...,-2,-1)", + sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),2,1); + test_cond("sdsrange(...,2,1)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),1,100); + test_cond("sdsrange(...,1,100)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),100,100); + test_cond("sdsrange(...,100,100)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("foo"); + y = sdsnew("foa"); + test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("bar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("aar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) + + { + int oldfree; + + sdsfree(x); + x = sdsnew("0"); + sh = (void*) (x-(sizeof(struct sdshdr))); + test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0); + x = sdsMakeRoomFor(x,1); + sh = (void*) (x-(sizeof(struct sdshdr))); + test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0); + oldfree = sh->free; + x[1] = '1'; + sdsIncrLen(x,1); + test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1'); + test_cond("sdsIncrLen() -- len", sh->len == 2); + test_cond("sdsIncrLen() -- free", sh->free == oldfree-1); + } + } + test_report() + return 0; +} +#endif diff --git a/src/sds.h b/src/sds.h new file mode 100644 index 0000000..5108f3a --- /dev/null +++ b/src/sds.h @@ -0,0 +1,135 @@ +/* SDSLib, A C dynamic strings library + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#ifndef __SDS_H +#define __SDS_H + +#define SDS_MAX_PREALLOC (1024*1024) + +//#include +#include +#include +#include + +typedef char *sds; + +struct sdshdr { + int len; + int free; + char buf[]; +}; + +static inline size_t sdslen(const sds s) { + struct sdshdr *sh = (struct sdshdr*)(s - (sizeof(struct sdshdr))); + return sh->len; +} + +static inline size_t sdsavail(const sds s) { + struct sdshdr *sh = (struct sdshdr*)(s - (sizeof(struct sdshdr))); + return sh->free; +} + +sds sdsnewlen(const void *init, size_t initlen); +sds sdsnew(const char *init); +sds sdsnewempty(size_t capacity); +sds sdsempty(void); +size_t sdslen(const sds s); +sds sdsdup(const sds s); +void sdsfree(sds s); +size_t sdsavail(const sds s); +sds sdsgrowzero(sds s, size_t len); +sds sdscatlen(sds s, const void *t, size_t len); +sds sdscat(sds s, const char *t); +sds sdscatsds(sds s, const sds t); +sds sdscpylen(sds s, const char *t, size_t len); +sds sdscpy(sds s, const char *t); + +sds sdscatvprintf(sds s, const char *fmt, va_list ap); +#ifdef __GNUC__ +sds sdscatprintf(sds s, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else +sds sdscatprintf(sds s, const char *fmt, ...); +#endif + +sds sdstrim(sds s, const char *cset); +void sdsrange(sds s, int start, int end); +void sdsupdatelen(sds s); +void sdsclear(sds s); +int sdscmp(const sds s1, const sds s2); +sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); +void sdsfreesplitres(sds *tokens, int count); +void sdstolower(sds s); +void sdstoupper(sds s); +sds sdsfromlonglong(long long value); +sds sdscatrepr(sds s, const char *p, size_t len); +sds *sdssplitargs(const char *line, int *argc); +sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); +sds sdsjoin(char **argv, int argc, char *sep); + +/* Low level functions exposed to the user API */ +sds sdsMakeRoomFor(sds s, size_t addlen); +void sdsIncrLen(sds s, int incr); +sds sdsRemoveFreeSpace(sds s); +size_t sdsAllocSize(sds s); + +template +sds sdscat(sds s, T t) { + struct sdshdr *sh; + size_t curlen = sdslen(s); + size_t len = sizeof(t); + + s = sdsMakeRoomFor(s, len); + if (s == NULL) return NULL; + sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + *(T*)(s + curlen) = t; + sh->len = curlen + len; + sh->free = sh->free - len; + s[curlen + len] = '\0'; + return s; +} + +template inline +T sdsread(sds s, size_t& rpos) { + struct sdshdr *sh; + sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + assert((size_t)sh->len >= rpos + sizeof(T)); + return *(T*)(s + rpos); +} + +static inline +const char* sdsread(sds s, size_t& rpos, size_t len) { + struct sdshdr *sh; + sh = (struct sdshdr*) (s - (sizeof(struct sdshdr))); + assert((size_t)sh->len >= rpos + len); + return (s + rpos); +} + +#endif diff --git a/src/utils.cpp b/src/utils.cpp index 7b3a171..beeb18a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,5 +1,8 @@ #include "common.h" #include "utils.h" +#ifndef CC_MSVC +#include +#endif char* filter_arg(char **param, int32_t *len) { char *begptr = *param; @@ -29,7 +32,7 @@ char* filter_arg(char **param, int32_t *len) { } } -extern void* nl_memdup(const void* src, uint32_t len) +void* nl_memdup(const void* src, uint32_t len) { if (src) { if (len != 0) { @@ -43,3 +46,102 @@ extern void* nl_memdup(const void* src, uint32_t len) } return NULL; } + +bool socket_host(uv_os_sock_t sock, bool local, char* host, uint32_t host_len, bool* ipv6, uint16_t* port) +{ + union sock_name_u { + sockaddr sock; + sockaddr_in sock4; + sockaddr_in6 sock6; + } sock_name; + int sock_len = sizeof(sock_name); +#ifdef CC_MSVC + int result = local ? getsockname(sock, (sockaddr*)&sock_name, &sock_len) : getpeername(sock, (sockaddr*)&sock_name, &sock_len); +#else + int result = local ? getsockname(sock, (sockaddr*)&sock_name, (socklen_t*)&sock_len) : getpeername(sock, (sockaddr*)&sock_name, (socklen_t*)&sock_len); +#endif + if (result != 0) + return false; + uint16_t family = sock_name.sock.sa_family; + if (family == AF_INET) { + if (host != NULL) { + uv_ip4_name(&sock_name.sock4, host, host_len); + } + if (ipv6 != NULL) { + *ipv6 = false; + } + if (port != NULL) { + *port = ntohs(sock_name.sock4.sin_port); + } + return true; + } + if (family == AF_INET6) { + if (host != NULL) { + uv_ip6_name(&sock_name.sock6, host, host_len); + } + if (ipv6 != NULL) { + *ipv6 = true; + } + if (port != NULL) { + *port = ntohs(sock_name.sock6.sin6_port); + } + return true; + } + return false; +} + +bool sockaddr_host(struct sockaddr* addr, char* host, uint32_t host_len, bool* ipv6, uint16_t* port) +{ + uint16_t family = addr->sa_family; + if (family == AF_INET) { + if (host != NULL) { + uv_ip4_name((sockaddr_in*)addr, host, host_len); + } + if (ipv6 != NULL) { + *ipv6 = false; + } + if (port != NULL) { + *port = ntohs(((sockaddr_in*)addr)->sin_port); + } + return true; + } + if (family == AF_INET6) { + if (host != NULL) { + uv_ip6_name((sockaddr_in6*)addr, host, host_len); + } + if (ipv6 != NULL) { + *ipv6 = true; + } + if (port != NULL) { + *port = ntohs(((sockaddr_in6*)addr)->sin6_port); + } + return true; + } + return false; +} + +uv_err_code host_sockaddr(bool ipv6, const char* host, uint16_t port, struct sockaddr* addr) +{ + if (!ipv6) { + struct sockaddr_in* addr4 = (struct sockaddr_in*)addr; + memset(addr4, 0, sizeof(struct sockaddr_in)); + addr4->sin_family = AF_INET; + addr4->sin_port = htons(port); + return uv_inet_pton(AF_INET, host, (void*)&addr4->sin_addr).code; + } else { + struct sockaddr_in6* addr6 = (struct sockaddr_in6*)addr; + memset(addr6, 0, sizeof(struct sockaddr_in6)); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(port); + return uv_inet_pton(AF_INET6, host, (void*)&addr6->sin6_addr).code; + } +} + +int get_pid() +{ +#ifdef CC_MSVC + return GetCurrentProcessId(); +#else + return getpid(); +#endif +} diff --git a/src/utils.h b/src/utils.h index dc9e3ea..7efafd5 100644 --- a/src/utils.h +++ b/src/utils.h @@ -4,5 +4,8 @@ extern char* filter_arg(char **param, int32_t *len); extern void* nl_memdup(const void* src, uint32_t len); - +extern bool socket_host(uv_os_sock_t sock, bool local, char* host, uint32_t host_len, bool* ipv6, uint16_t* port); +extern bool sockaddr_host(struct sockaddr* addr, char* host, uint32_t host_len, bool* ipv6, uint16_t* port); +extern uv_err_code host_sockaddr(bool ipv6, const char* host, uint16_t port, struct sockaddr* addr); +extern int get_pid(); #endif diff --git a/src/uv_handle_base.cpp b/src/uv_handle_base.cpp index b133881..70879b5 100644 --- a/src/uv_handle_base.cpp +++ b/src/uv_handle_base.cpp @@ -70,7 +70,7 @@ void uv_handle_base_t::close() if (m_handle) { uv_close(m_handle, on_closed); } else if (m_lua_ref != LUA_REFNIL) { - singleton_ref(node_lua_t).context_send(m_source, 0, m_lua_ref, RESPONSE_HANDLE_CLOSE, (double)get_handle_set()); + singleton_ref(node_lua_t).context_send(m_source, 0, m_lua_ref, RESPONSE_HANDLE_CLOSE, (int64_t)get_handle_set()); delete this; } } @@ -79,7 +79,7 @@ void uv_handle_base_t::on_closed(uv_handle_t* handle) { uv_handle_base_t* tcp_handle = (uv_handle_base_t*)handle->data; if (tcp_handle->m_lua_ref != LUA_REFNIL) { - singleton_ref(node_lua_t).context_send(tcp_handle->m_source, 0, tcp_handle->m_lua_ref, RESPONSE_HANDLE_CLOSE, (double)(tcp_handle->get_handle_set())); + singleton_ref(node_lua_t).context_send(tcp_handle->m_source, 0, tcp_handle->m_lua_ref, RESPONSE_HANDLE_CLOSE, (int64_t)(tcp_handle->get_handle_set())); } delete tcp_handle; } diff --git a/src/uv_tcp_handle.cpp b/src/uv_tcp_handle.cpp index d5a8a31..5edae7a 100644 --- a/src/uv_tcp_handle.cpp +++ b/src/uv_tcp_handle.cpp @@ -2,6 +2,17 @@ #include "network.h" #include "node_lua.h" #include "uv_tcp_handle.h" +#include "lua_tcp_handle.h" + +#if defined (CC_MSVC) +#define uv_tcp_fd(handle) ((handle)->socket) +#elif defined(__APPLE__) +extern "C" int uv___stream_fd(uv_stream_t* handle); +#define uv_tcp_fd(handle) (uv___stream_fd((uv_stream_t*) (handle))) +#else +#define uv_tcp_fd(handle) ((handle)->io_watcher.fd) +#endif /* defined(__APPLE__) */ + void uv_tcp_listen_handle_t::on_accept(uv_stream_t* server, int status) { @@ -21,7 +32,21 @@ void uv_tcp_listen_handle_t::listen_tcp(request_tcp_listen_t& request) #ifdef CC_MSVC uv_tcp_simultaneous_accepts(server, 0); #endif - if ((!request.m_ipv6 ? uv_tcp_bind(server, uv_ip4_addr(REQUEST_SPARE_PTR(request), request.m_port)) == 0 : uv_tcp_bind6(server, uv_ip6_addr(REQUEST_SPARE_PTR(request), request.m_port))) && uv_listen((uv_stream_t*)server, request.m_backlog, on_accept) == 0) { + + uv_os_sock_t sock; + if (network_t::make_tcp_socket(&sock, request.m_ipv6, request.m_reuse)) { + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_LISTEN, NL_ESOCKFAIL); + uv_close((uv_handle_t*)server, on_closed); + return; + } + if (uv_tcp_open(server, sock)) { + network_t::close_socket(sock); + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_LISTEN, singleton_ref(network_t).last_error()); + uv_close((uv_handle_t*)server, on_closed); + return; + } + + if ((!request.m_ipv6 ? uv_tcp_bind(server, uv_ip4_addr(REQUEST_SPARE_PTR(request), request.m_port)) == 0 : uv_tcp_bind6(server, uv_ip6_addr(REQUEST_SPARE_PTR(request), request.m_port)) == 0) && uv_listen((uv_stream_t*)server, 0x7fffffff, on_accept) == 0) { if (!singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_LISTEN, (void*)this)) { uv_close((uv_handle_t*)server, on_closed); } @@ -59,6 +84,9 @@ void uv_tcp_listen_handle_t::try_accept() uv_tcp_socket_handle_t *client_handle = new uv_tcp_socket_handle_t(m_handle->loop, m_source, (m_handle->type == UV_NAMED_PIPE)); uv_handle_t *client = (uv_handle_t*)client_handle->m_handle; if (uv_accept((uv_stream_t*)(m_handle), (uv_stream_t*)client) == 0) { + if (m_handle->type == UV_TCP) { + client_handle->m_tcp_sock = uv_tcp_fd((uv_tcp_t*)client); + } if (!singleton_ref(node_lua_t).context_send(m_source, 0, m_lua_ref, RESPONSE_TCP_ACCEPT, (void*)client_handle)) { uv_close((uv_handle_t*)client, on_closed); } else if (m_blocking_accept_count > 0) { @@ -82,25 +110,18 @@ void uv_tcp_listen_handle_t::accept(request_tcp_accept_t& request) ///////////////////////////////////////////////////////////////////////////////////////////////////////// #define LARGE_UNCOMPLETE_LIMIT (1 * 1024) -#define SHARED_READ_BUFFER_SIZE (64 * 1024) -uv_buf_t uv_tcp_socket_handle_t::m_shared_read_buffer = { 0, NULL, }; -void uv_tcp_socket_handle_t::make_shared_read_buffer() -{ - if (!m_shared_read_buffer.base) { - m_shared_read_buffer.base = (char*)nl_malloc(SHARED_READ_BUFFER_SIZE); - assert(m_shared_read_buffer.base != NULL); - m_shared_read_buffer.len = SHARED_READ_BUFFER_SIZE; - } -} +typedef struct { + uv_getaddrinfo_t m_request; + uv_tcp_socket_handle_t *m_client; + uint32_t m_session; +} uv_addr_resolver_t; -void uv_tcp_socket_handle_t::free_shared_read_buffer() +uv_tcp_socket_handle_t::~uv_tcp_socket_handle_t() { - if (m_shared_read_buffer.base) { - nl_free(m_shared_read_buffer.base); - m_shared_read_buffer.base = NULL; - m_shared_read_buffer.len = 0; - } + clear_read_cached_buffers(); + clear_write_cached_requests(); + singleton_ref(network_t).pop_shared_write_socket(SOCKET_MAKE_FD(m_lua_ref, m_source)); } void uv_tcp_socket_handle_t::on_connect(uv_connect_t* req, int status) @@ -109,6 +130,9 @@ void uv_tcp_socket_handle_t::on_connect(uv_connect_t* req, int status) uv_tcp_socket_handle_t *client_handle = (uv_tcp_socket_handle_t*)client->data; uint32_t session = (uint64_t)req->data; if (status == 0) { //connect success + if (client->type == UV_TCP) { + client_handle->m_tcp_sock = uv_tcp_fd((uv_tcp_t*)client); + } if (!singleton_ref(node_lua_t).context_send(client_handle->m_source, 0, session, RESPONSE_TCP_CONNECT, (void*)client_handle)) { uv_close((uv_handle_t*)client, on_closed); } @@ -119,6 +143,29 @@ void uv_tcp_socket_handle_t::on_connect(uv_connect_t* req, int status) nl_free(req); } +void uv_tcp_socket_handle_t::on_resolve(uv_getaddrinfo_t* req, int status, struct addrinfo* res) +{ + uv_addr_resolver_t* resolver = (uv_addr_resolver_t*)(req->data); + uv_tcp_socket_handle_t *client_handle = (uv_tcp_socket_handle_t*)resolver->m_client; + uv_tcp_t* client = (uv_tcp_t*)client_handle->m_handle; + if (status == 0) { + struct sockaddr_in* addr = (struct sockaddr_in*)res->ai_addr; + uv_connect_t* req = (uv_connect_t*)nl_malloc(sizeof(*req)); + req->data = (void*)resolver->m_session; + if ((addr->sin_family == AF_INET) ? uv_tcp_connect(req, client, *(struct sockaddr_in*)addr, on_connect) != 0 : uv_tcp_connect6(req, client, *(struct sockaddr_in6*)addr, on_connect) != 0) { + singleton_ref(node_lua_t).context_send(client_handle->m_source, 0, resolver->m_session, RESPONSE_TCP_CONNECT, singleton_ref(network_t).last_error()); + uv_close((uv_handle_t*)client, on_closed); + nl_free(req); + } + nl_free(resolver); + uv_freeaddrinfo(res); + } else { + singleton_ref(node_lua_t).context_send(client_handle->m_source, 0, resolver->m_session, RESPONSE_TCP_CONNECT, singleton_ref(network_t).last_error()); + uv_close((uv_handle_t*)client, on_closed); + nl_free(resolver); + } +} + void uv_tcp_socket_handle_t::connect_tcp(request_tcp_connect_t& request) { uv_tcp_t *client = (uv_tcp_t*)m_handle; @@ -131,12 +178,39 @@ void uv_tcp_socket_handle_t::connect_tcp(request_tcp_connect_t& request) return; } } - uv_connect_t* req = (uv_connect_t*)nl_malloc(sizeof(*req)); - req->data = (void*)request.m_session; - if (!request.m_remote_ipv6 ? uv_tcp_connect(req, client, uv_ip4_addr(remote_host, request.m_remote_port), on_connect) != 0 : uv_tcp_connect6(req, client, uv_ip6_addr(remote_host, request.m_remote_port), on_connect) != 0) { - singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_CONNECT, singleton_ref(network_t).last_error()); + union { + sockaddr_in addr4; + sockaddr_in6 addr6; + } sock_addr; + uv_err_code err = host_sockaddr(request.m_remote_ipv6, remote_host, request.m_remote_port, (struct sockaddr*)&sock_addr); + if (err == UV_OK) { + uv_connect_t* req = (uv_connect_t*)nl_malloc(sizeof(*req)); + req->data = (void*)request.m_session; + if (!request.m_remote_ipv6 ? uv_tcp_connect(req, client, sock_addr.addr4, on_connect) != 0 : uv_tcp_connect6(req, client, sock_addr.addr6, on_connect) != 0) { + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_CONNECT, singleton_ref(network_t).last_error()); + uv_close((uv_handle_t*)client, on_closed); + nl_free(req); + } + } else if (err == UV_EINVAL) { //try resolve host name + char port[8]; + struct addrinfo hints; + hints.ai_family = !request.m_remote_ipv6 ? AF_INET : AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = 0; + uv_addr_resolver_t* resolver = (uv_addr_resolver_t*)nl_malloc(sizeof(uv_addr_resolver_t)); + sprintf(port, "%u", request.m_remote_port); + resolver->m_client = this; + resolver->m_session = request.m_session; + resolver->m_request.data = resolver; + if (uv_getaddrinfo(client->loop, &resolver->m_request, on_resolve, remote_host, port, &hints) != 0) { + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_CONNECT, singleton_ref(network_t).last_error()); + uv_close((uv_handle_t*)client, on_closed); + nl_free(resolver); + } + } else { + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_CONNECT, err); uv_close((uv_handle_t*)client, on_closed); - nl_free(req); } } @@ -161,19 +235,50 @@ void uv_tcp_socket_handle_t::on_write(uv_write_t* req, int status) } else { buffer_release(uv_request->m_buffer); } - if (uv_request->m_session != LUA_REFNIL) { - singleton_ref(node_lua_t).context_send(socket_handle->m_source, 0, uv_request->m_session, RESPONSE_TCP_WRITE, status == 0 ? UV_OK : socket_handle->m_write_error); + if (uv_request->m_session != (uint32_t)LUA_REFNIL) { + singleton_ref(node_lua_t).context_send(uv_request->m_source, 0, uv_request->m_session, RESPONSE_TCP_WRITE, status == 0 ? UV_OK : socket_handle->m_write_error); } socket_handle->put_write_cached_request(uv_request); } -/* We must response something to lua-service even error occurred. */ void uv_tcp_socket_handle_t::write(request_tcp_write_t& request) +{ + int32_t err = UV_UNKNOWN; + if (request.m_shared_write) { + uv_tcp_socket_handle_t* handle = (uv_tcp_socket_handle_t*)singleton_ref(network_t).get_shared_write_socket(request.m_socket_fd); + if (handle != NULL) { + uint32_t length = request.m_length > 0 ? request.m_length : (uint32_t)buffer_data_length(request.m_buffer); + if (!check_head_option_max(handle->m_write_head_option, length)) { + err = NL_ETCPWRITELONG; + } else { + err = handle->write_handle(request); + } + } else { + err = NL_ETCPNOWSHARED; + } + } else { + err = request.m_socket_handle->write_handle(request); + } + if (err != UV_OK) { /* write error had been occurred */ + if (request.m_length > 0) { + nl_free((void*)request.m_string); + } else { + buffer_release(request.m_buffer); + } + if (request.m_session != (uint32_t)LUA_REFNIL) { + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_TCP_WRITE, (nl_err_code)err); + } + } +} + +/* We must response something to lua-service even error occurred. */ +int32_t uv_tcp_socket_handle_t::write_handle(request_tcp_write_t& request) { int result; if (m_write_error == UV_OK) { uv_buf_t uv_buf[] = { { 0, NULL }, { 0, NULL } }; write_uv_request_t* uv_request = get_write_cached_request(); + uv_request->m_source = request.m_source; uv_request->m_session = request.m_session; uv_request->m_length = request.m_length; if (request.m_length > 0) { @@ -193,29 +298,18 @@ void uv_tcp_socket_handle_t::write(request_tcp_write_t& request) } else { result = uv_write(&uv_request->m_write_req, (uv_stream_t*)(m_handle), uv_buf + 1, 1, on_write); } - if (result == 0) return; + if (result == 0) return UV_OK; m_write_error = singleton_ref(network_t).last_error(); /* write error occurs */ put_write_cached_request(uv_request); } - /* write error had been occurred */ - if (request.m_length > 0) { - nl_free((void*)request.m_string); - } else { - buffer_release(request.m_buffer); - } - if (request.m_session != LUA_REFNIL) { - singleton_ref(node_lua_t).context_send(m_source, 0, request.m_session, RESPONSE_TCP_WRITE, m_write_error); - } + return m_write_error; } uv_buf_t uv_tcp_socket_handle_t::on_read_alloc(uv_handle_t* handle, size_t suggested_size) { uv_tcp_socket_handle_t *socket_handle = (uv_tcp_socket_handle_t*)(handle->data); if (buffer_write_size(socket_handle->m_read_buffer) < LARGE_UNCOMPLETE_LIMIT) { - if (!m_shared_read_buffer.base) { - make_shared_read_buffer(); - } - return m_shared_read_buffer; + return singleton_ref(network_t).make_shared_read_buffer(); } /* read in m_shared_read_buffer but not complete large pack(optimization for memcpy) */ uv_buf_t buf; @@ -234,7 +328,7 @@ void uv_tcp_socket_handle_t::write_read_buffer(ssize_t nread, uv_buf_t buf) return; } char* buffer = buf.base; - if (buffer != m_shared_read_buffer.base) { + if (buffer != singleton_ref(network_t).get_shared_read_buffer().base) { buffer_adjust_len(m_read_buffer, nread); if (buffer_write_size(m_read_buffer) == 0) { write_read_buffer_finish(UV_OK); @@ -294,7 +388,8 @@ void uv_tcp_socket_handle_t::write_read_buffer(ssize_t nread, uv_buf_t buf) void uv_tcp_socket_handle_t::clear_read_cached_buffers() { buffer_release(m_read_buffer); - for (int i = 0; i < m_read_cached_buffers.size(); ++i) { + int size = m_read_cached_buffers.size(); + for (int i = 0; i < size; ++i) { buffer_release(m_read_cached_buffers.front()); m_read_cached_buffers.pop(); } @@ -322,14 +417,30 @@ void uv_tcp_socket_handle_t::write_read_buffer_finish(uv_err_code read_error) } } else { if (m_read_error == UV_OK) { - m_read_error = read_error; - singleton_ref(node_lua_t).context_send(m_source, 0, m_lua_ref, RESPONSE_TCP_READ, m_read_error); + trigger_read_error(read_error); uv_read_stop((uv_stream_t*)m_handle); + } else { + clear_read_cached_buffers(); } - clear_read_cached_buffers(); } } +//when read error occurred, we need to send the cached buffer to lua_context in case of half closed peer socket. +//peer socket may only shut down write(half close) before totally close the socket. +void uv_tcp_socket_handle_t::trigger_read_error(uv_err_code read_error) +{ + int size = m_read_cached_buffers.size(); + for (int i = 0; i < size; ++i) { + singleton_ref(node_lua_t).context_send_buffer_release(m_source, 0, m_lua_ref, RESPONSE_TCP_READ, m_read_cached_buffers.front()); + m_read_cached_buffers.pop(); + } + buffer_release(m_read_buffer); + buffer_make_invalid(m_read_buffer); + singleton_ref(node_lua_t).context_send(m_source, 0, m_lua_ref, RESPONSE_TCP_READ, read_error); + atomic_barrier(); + m_read_error = read_error; +} + /* When RESPONSE_TCP_READ error occurs, read request must also stop in context_lua thread. */ void uv_tcp_socket_handle_t::on_read(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { @@ -348,7 +459,8 @@ void uv_tcp_socket_handle_t::read(request_tcp_read_t& request) } else { //non-blocking has started m_has_noblocking_read = true; } - for (int i = 0; i < m_read_cached_buffers.size(); ++i) { + int size = m_read_cached_buffers.size(); + for (int i = 0; i < size; ++i) { singleton_ref(node_lua_t).context_send_buffer_release(m_source, 0, m_lua_ref, RESPONSE_TCP_READ, m_read_cached_buffers.front()); m_read_cached_buffers.pop(); if (m_blocking_read_count > 0) { @@ -360,9 +472,7 @@ void uv_tcp_socket_handle_t::read(request_tcp_read_t& request) } if (!read_started && uv_read_start((uv_stream_t*)(m_handle), on_read_alloc, on_read) != 0) { if (!uv_is_closing((uv_handle_t*)(m_handle))) { - m_read_error = singleton_ref(network_t).last_error(); - singleton_ref(node_lua_t).context_send(m_source, 0, m_lua_ref, RESPONSE_TCP_READ, m_read_error); - clear_read_cached_buffers(); + trigger_read_error(singleton_ref(network_t).last_error()); } } } @@ -375,6 +485,9 @@ void uv_tcp_socket_handle_t::set_option(uint8_t type, const char *option) case OPT_TCP_NODELAY: set_tcp_nodelay(*option); break; + case OPT_TCP_WSHARED: + set_tcp_wshared(*option); + break; default: break; } @@ -387,3 +500,14 @@ void uv_tcp_socket_handle_t::set_tcp_nodelay(bool enable) } } +void uv_tcp_socket_handle_t::set_tcp_wshared(bool enable) +{ + if (!uv_is_closing((uv_handle_t*)(m_handle))) { + int64_t fd = SOCKET_MAKE_FD(m_lua_ref, m_source); + if (enable) { + singleton_ref(network_t).put_shared_write_socket(fd, this); + } else { + singleton_ref(network_t).pop_shared_write_socket(fd); + } + } +} diff --git a/src/uv_tcp_handle.h b/src/uv_tcp_handle.h index 1517886..2ebb000 100644 --- a/src/uv_tcp_handle.h +++ b/src/uv_tcp_handle.h @@ -28,6 +28,7 @@ typedef struct { enum tcp_option_type { OPT_TCP_NODELAY, + OPT_TCP_WSHARED, }; class uv_tcp_listen_handle_t : public uv_handle_base_t @@ -59,6 +60,7 @@ class uv_tcp_socket_handle_t : public uv_handle_base_t public: uv_tcp_socket_handle_t(uv_loop_t* loop, uint32_t source, bool sock) : uv_handle_base_t(loop, (sock ? UV_NAMED_PIPE : UV_TCP), source), + m_tcp_sock(-1), m_has_noblocking_read(false), m_blocking_read_count(0), m_read_error(UV_OK), @@ -70,40 +72,40 @@ class uv_tcp_socket_handle_t : public uv_handle_base_t init_head_option(m_write_head_option); buffer_make_invalid(m_read_buffer); } + ~uv_tcp_socket_handle_t(); friend class uv_tcp_listen_handle_t; friend class lua_tcp_socket_handle_t; private: static void on_connect(uv_connect_t* req, int status); + static void on_resolve(uv_getaddrinfo_t* req, int status, struct addrinfo* res); static void on_write(uv_write_t* req, int status); static void on_read(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); - static uv_buf_t on_read_alloc(uv_handle_t* handle, size_t suggested_size); /* return tcp shared read buffer */ + static uv_buf_t on_read_alloc(uv_handle_t* handle, size_t suggested_size); /* return shared read buffer */ + int32_t write_handle(request_tcp_write_t& request); void write_read_buffer(ssize_t nread, uv_buf_t buf); void write_read_buffer_finish(uv_err_code err_code); + void trigger_read_error(uv_err_code read_error); void clear_read_cached_buffers(); + public: void connect_tcp(request_tcp_connect_t& request); void connect_sock(request_tcp_connects_t& request); - void write(request_tcp_write_t& request); void read(request_tcp_read_t& request); + static void write(request_tcp_write_t& request); public: - ~uv_tcp_socket_handle_t() - { - clear_read_cached_buffers(); - clear_write_cached_requests(); - } bool read_start_state() const { return m_has_noblocking_read || m_blocking_read_count > 0; } void set_option(uint8_t type, const char *option); void set_tcp_nodelay(bool enable); + void set_tcp_wshared(bool enable); uv_err_code get_read_error() const { return m_read_error; } uv_err_code get_write_error() const { return m_write_error; } - static void free_shared_read_buffer(); - static void make_shared_read_buffer(); private: + uv_os_sock_t m_tcp_sock; /* read parameters */ bool m_has_noblocking_read; /* can't change to false if set to be true */ uint32_t m_blocking_read_count; /* maybe not so precise only if m_has_noblocking_accept has been set true, @@ -115,7 +117,6 @@ class uv_tcp_socket_handle_t : public uv_handle_base_t uint32_t m_read_bytes; buffer_t m_read_buffer; std::queue m_read_cached_buffers; - static uv_buf_t m_shared_read_buffer; /* tcp shared read buffer in network thread */ /* the following members are read-acceleration options and set in context thread before recv starts. */ head_option_t m_read_head_option; @@ -123,12 +124,13 @@ class uv_tcp_socket_handle_t : public uv_handle_base_t private: typedef struct { uv_write_t m_write_req; - uint32_t m_session; - uint32_t m_length; union { const char* m_string; buffer_t m_buffer; }; + uint32_t m_length; /* judge it's string or buffer */ + uint32_t m_source; + uint32_t m_session; int8_t m_head_buffer[4]; } write_uv_request_t; @@ -159,9 +161,8 @@ class uv_tcp_socket_handle_t : public uv_handle_base_t } m_write_cached_reqs.push_back(request); } - void clear_write_cached_requests() - { - for (int i = 0; i < m_write_cached_reqs.size(); ++i) { + void clear_write_cached_requests() { + for (size_t i = 0; i < m_write_cached_reqs.size(); ++i) { nl_free(m_write_cached_reqs[i]); } m_write_cached_reqs.clear(); diff --git a/src/uv_timer_handle.h b/src/uv_timer_handle.h index 287c414..2d6d4ab 100644 --- a/src/uv_timer_handle.h +++ b/src/uv_timer_handle.h @@ -13,4 +13,4 @@ class uv_timer_handle_t : public uv_handle_base_t static void on_timedout(uv_timer_t* handle, int status); }; -#endif \ No newline at end of file +#endif diff --git a/src/uv_udp_handle.cpp b/src/uv_udp_handle.cpp new file mode 100644 index 0000000..56a0bb1 --- /dev/null +++ b/src/uv_udp_handle.cpp @@ -0,0 +1,163 @@ +#include "message.h" +#include "network.h" +#include "node_lua.h" +#include "uv_udp_handle.h" + +#if defined (CC_MSVC) +#define uv_udp_fd(handle) ((handle)->socket) +#else +#define uv_udp_fd(handle) ((handle)->io_watcher.fd) +#endif + +uv_udp_handle_t::~uv_udp_handle_t() +{ + clear_write_cached_requests(); + singleton_ref(network_t).pop_shared_write_socket(SOCKET_MAKE_FD(m_lua_ref, m_source)); +} + +void uv_udp_handle_t::open(request_udp_open_t& request) +{ + uv_udp_t* server = (uv_udp_t*)m_handle; + if ((!request.m_ipv6 ? uv_udp_bind(server, uv_ip4_addr(REQUEST_SPARE_PTR(request), request.m_port), 0) == 0 : uv_udp_bind6(server, uv_ip6_addr(REQUEST_SPARE_PTR(request), request.m_port), 0) == 0)) { + m_udp_sock = uv_udp_fd((uv_udp_t*)server); + if (!singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_UDP_OPEN, (void*)this)) { + uv_close((uv_handle_t*)server, on_closed); + } + } else { + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_UDP_OPEN, singleton_ref(network_t).last_error()); + uv_close((uv_handle_t*)server, on_closed); + } +} + +uv_buf_t uv_udp_handle_t::on_read_alloc(uv_handle_t* handle, size_t suggested_size) +{ + return singleton_ref(network_t).make_shared_read_buffer(); +} + +void uv_udp_handle_t::on_read(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, struct sockaddr* addr, unsigned flags) +{ + /* udp max datagram read pack size: SHARED_READ_BUFFER_SIZE(64 * 1024) */ + if (nread == 0) { + return; + } + uv_udp_handle_t* udp_handle = (uv_udp_handle_t*)(handle->data); + if (nread == -1) { + singleton_ref(node_lua_t).context_send(udp_handle->m_source, 0, udp_handle->m_lua_ref, RESPONSE_UDP_READ, singleton_ref(network_t).last_error()); + return; + } + buffer_t buffer = buffer_new(nread, buf.base, nread); + char* host = (char*)nl_malloc(64); + uint16_t port = 0; + bool ipv6 = false; + *host = '\0'; + sockaddr_host(addr, host, 64, &ipv6, &port); + message_array_t* array = message_array_create(4); + array->m_array[0] = message_t(0, udp_handle->m_lua_ref, RESPONSE_UDP_READ, buffer); + array->m_array[1] = message_t(0, udp_handle->m_lua_ref, RESPONSE_UDP_READ, host); + array->m_array[2] = message_t(0, udp_handle->m_lua_ref, RESPONSE_UDP_READ, (int64_t)port); + array->m_array[3] = message_t(0, udp_handle->m_lua_ref, RESPONSE_UDP_READ, ipv6); + singleton_ref(node_lua_t).context_send_array_release(udp_handle->m_source, 0, udp_handle->m_lua_ref, RESPONSE_UDP_READ, array); +} + +void uv_udp_handle_t::read(request_udp_read_t& request) +{ + if (!m_read_started) { + if (uv_udp_recv_start((uv_udp_t*)m_handle, on_read_alloc, on_read) != 0) { + singleton_ref(node_lua_t).context_send(m_source, 0, m_lua_ref, RESPONSE_UDP_READ, singleton_ref(network_t).last_error()); + return; + } + m_read_started = true; + } +} + +void uv_udp_handle_t::on_write(uv_udp_send_t* req, int status) +{ + write_uv_request_t *uv_request = (write_uv_request_t*)req->data; + uv_udp_handle_t *socket_handle = (uv_udp_handle_t*)(req->handle->data); + if (uv_request->m_length > 0) { + nl_free((void*)uv_request->m_string); + } else { + buffer_release(uv_request->m_buffer); + } + if (uv_request->m_session != (uint32_t)LUA_REFNIL) { + singleton_ref(node_lua_t).context_send(uv_request->m_source, 0, uv_request->m_session, RESPONSE_UDP_WRITE, status == 0 ? UV_OK : singleton_ref(network_t).last_error()); + } + socket_handle->put_write_cached_request(uv_request); +} + +void uv_udp_handle_t::write(request_udp_write_t& request) +{ + int32_t err = UV_UNKNOWN; + if (request.m_shared_write) { + uv_udp_handle_t* handle = (uv_udp_handle_t*)singleton_ref(network_t).get_shared_write_socket(request.m_socket_fd); + if (handle != NULL) { + err = handle->write_handle(request); + } else { + err = NL_EUDPNOWSHARED; + } + } else { + err = request.m_socket_handle->write_handle(request); + } + if (err != UV_OK) { /* write error had been occurred */ + if (request.m_length > 0) { + nl_free((void*)request.m_string); + } else { + buffer_release(request.m_buffer); + } + if (request.m_session != (uint32_t)LUA_REFNIL) { + singleton_ref(node_lua_t).context_send(request.m_source, 0, request.m_session, RESPONSE_UDP_WRITE, (nl_err_code)err); + } + } +} + +int32_t uv_udp_handle_t::write_handle(request_udp_write_t& request) +{ + int result; + uv_buf_t uv_buf; + write_uv_request_t* uv_request = get_write_cached_request(); + uv_request->m_source = request.m_source; + uv_request->m_session = request.m_session; + uv_request->m_length = request.m_length; + if (request.m_length > 0) { + uv_request->m_string = request.m_string; + uv_buf.len = request.m_length; + uv_buf.base = (char*)request.m_string; + } else { + uv_request->m_buffer = request.m_buffer; + uv_buf.len = buffer_data_length(request.m_buffer); + uv_buf.base = buffer_data_ptr(request.m_buffer); + } + if (!request.m_ipv6) { + result = uv_udp_send(&uv_request->m_write_req, (uv_udp_t*)m_handle, &uv_buf, 1, uv_ip4_addr(REQUEST_SPARE_PTR(request), request.m_port), on_write); + } else { + result = uv_udp_send6(&uv_request->m_write_req, (uv_udp_t*)m_handle, &uv_buf, 1, uv_ip6_addr(REQUEST_SPARE_PTR(request), request.m_port), on_write); + } + if (result == 0) return UV_OK; + put_write_cached_request(uv_request); + return singleton_ref(network_t).last_error(); /* write error occurs */ +} + +void uv_udp_handle_t::set_option(uint8_t type, const char *option) +{ + udp_option_type opt_type = (udp_option_type)type; + switch (opt_type) + { + case OPT_UDP_WSHARED: + set_udp_wshared(*option); + break; + default: + break; + } +} + +void uv_udp_handle_t::set_udp_wshared(bool enable) +{ + if (!uv_is_closing((uv_handle_t*)(m_handle))) { + int64_t fd = SOCKET_MAKE_FD(m_lua_ref, m_source); + if (enable) { + singleton_ref(network_t).put_shared_write_socket(fd, this); + } else { + singleton_ref(network_t).pop_shared_write_socket(fd); + } + } +} diff --git a/src/uv_udp_handle.h b/src/uv_udp_handle.h new file mode 100644 index 0000000..967033c --- /dev/null +++ b/src/uv_udp_handle.h @@ -0,0 +1,82 @@ +#ifndef UV_UDP_HANDLE_H_ +#define UV_UDP_HANDLE_H_ +#include "buffer.h" +#include "request.h" +#include "uv_handle_base.h" +#include + +#define UDP_WRITE_UV_REQUEST_CACHE_MAX 32 + +enum udp_option_type { + OPT_UDP_WSHARED, +}; + +class uv_udp_handle_t : public uv_handle_base_t +{ +public: + uv_udp_handle_t(uv_loop_t* loop, uint32_t source) + : uv_handle_base_t(loop, UV_UDP, source), + m_read_started(false), + m_udp_sock(-1) {} + ~uv_udp_handle_t(); + + friend class lua_udp_handle_t; +private: + static void on_read(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, struct sockaddr* addr, unsigned flags); + static uv_buf_t on_read_alloc(uv_handle_t* handle, size_t suggested_size); + static void on_write(uv_udp_send_t* req, int status); + + int32_t write_handle(request_udp_write_t& request); +public: + void set_option(uint8_t type, const char *option); + void set_udp_wshared(bool enable); + +public: + void open(request_udp_open_t& request); + void read(request_udp_read_t& request); + static void write(request_udp_write_t& request); +private: + bool m_read_started; + uv_os_sock_t m_udp_sock; + + typedef struct { + uv_udp_send_t m_write_req; + union { + const char* m_string; + buffer_t m_buffer; + }; + uint32_t m_length; /* judge it's string or buffer */ + uint32_t m_source; + uint32_t m_session; + } write_uv_request_t; + + std::vector m_write_cached_reqs; + + write_uv_request_t* get_write_cached_request() { + write_uv_request_t* cache; + if (!m_write_cached_reqs.empty()) { + cache = m_write_cached_reqs.back(); + m_write_cached_reqs.pop_back(); + } + else { + cache = (write_uv_request_t*)nl_malloc(sizeof(write_uv_request_t)); + cache->m_write_req.data = cache; + } + return cache; + } + void put_write_cached_request(write_uv_request_t* request) { + if (m_write_cached_reqs.size() >= UDP_WRITE_UV_REQUEST_CACHE_MAX) { + nl_free(m_write_cached_reqs.back()); + m_write_cached_reqs.pop_back(); + } + m_write_cached_reqs.push_back(request); + } + void clear_write_cached_requests() { + for (size_t i = 0; i < m_write_cached_reqs.size(); ++i) { + nl_free(m_write_cached_reqs[i]); + } + m_write_cached_reqs.clear(); + } +}; + +#endif diff --git a/stap/sample-bt b/stap/sample-bt new file mode 100644 index 0000000..65b31ef --- /dev/null +++ b/stap/sample-bt @@ -0,0 +1,298 @@ +#!/usr/bin/env perl + +# Copyright (C) Yichun Zhang (agentzh) + +use 5.006001; +use strict; +use warnings; + +use Getopt::Std qw( getopts ); + +my %opts; + +getopts("a:dhl:p:t:uk", \%opts) + or die usage(); + +if ($opts{h}) { + print usage(); + exit; +} + +my $pid = $opts{p} + or die "No process pid specified by the -p option.\n"; + +if ($pid !~ /^\d+$/) { + die "Bad -p option value \"$pid\": not look like a pid.\n"; +} + +my $condition = "pid() == target()"; + +my $time = $opts{t} + or die "No -t option specified.\n"; + +my $limit = $opts{l} || 1024; + +my $user_space = $opts{u}; +my $kernel_space = $opts{k}; + +if (!$user_space && !$kernel_space) { + die "Neither -u nor -k is specified.\n", + "(You should choose to sample in the user space or ", + "in the kernel space or in both.)\n"; +} + +if ($time !~ /^\d+$/) { + die "Bad time value specified in the -t option: $time\n"; +} + +my $stap_args = $opts{a} || ''; + +if ($stap_args !~ /(?:^|\s)-D\s*MAXACTION=/) { + $stap_args .= " -DMAXACTION=100000" +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXMAPENTRIES=/) { + $stap_args .= " -DMAXMAPENTRIES=5000" +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXBACKTRACE=/) { + $stap_args .= " -DMAXBACKTRACE=200" +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXSTRINGLEN=2048/) { + $stap_args .= " -DMAXSTRINGLEN=2048" +} + +#warn $stap_args; + +if ($^O ne 'linux') { + die "Only linux is supported but I am on $^O.\n"; +} + +my $exec_file = "/proc/$pid/exe"; +if (!-f $exec_file) { + die "Nginx process $pid is not running or ", + "you do not have enough permissions.\n"; +} + +my $nginx_path = readlink $exec_file; + +my $ver = `stap --version 2>&1`; +if (!defined $ver) { + die "Systemtap not installed or its \"stap\" utility is not visible to the PATH environment: $!\n"; +} + +if ($ver =~ /version\s+(\d+\.\d+)/i) { + my $v = $1; + if ($v < 2.1) { + die "ERROR: at least systemtap 2.1 is required but found $v\n"; + } + +} else { + die "ERROR: unknown version of systemtap:\n$ver\n"; +} + +my $context; +if ($user_space) { + if ($kernel_space) { + $context = 'both user-space and kernel-space'; + + } else { + $context = 'user-space only'; + } + +} else { + $context = 'kernel-space only'; +} + +my $preamble = <<_EOC_; +probe begin { + warn(sprintf("Tracing %d ($nginx_path) in $context...\\n", target())) +} +_EOC_ + +my $stap_src; + +if ($user_space) { + if ($kernel_space) { + # in both user-space and kernel-space + $stap_src = <<_EOC_; +$preamble + +global bts; +global quit = 0 + +probe timer.profile { + if ($condition) { + if (!quit) { + bts[backtrace(), ubacktrace()] <<< 1 + + } else { + + foreach ([sys, usr] in bts- limit $limit) { + print_stack(sys) + print_ustack(usr) + printf("\\t%d\\n", \@count(bts[sys, usr])) + } + + exit() + } + } +} + +probe timer.s($time) { + nstacks = 0 + foreach ([a, b] in bts limit 1) { + nstacks++ + } + + if (nstacks == 0) { + warn("No backtraces found. Quitting now...\\n") + exit() + + } else { + warn("Time's up. Quitting now...(it may take a while)\\n") + quit = 1 + } +} +_EOC_ + + } else { + # in user-space only + $stap_src = <<_EOC_; +$preamble + +global bts; +global quit = 0; + +probe timer.profile { + if ($condition) { + if (!quit) { + bts[ubacktrace()] <<< 1; + + } else { + + foreach (bt in bts- limit $limit) { + print_ustack(bt); + printf("\\t%d\\n", \@count(bts[bt])); + } + + exit() + } + } +} + +probe timer.s($time) { + nstacks = 0 + foreach (bt in bts limit 1) { + nstacks++ + } + + if (nstacks == 0) { + warn("No backtraces found. Quitting now...\\n") + exit() + + } else { + warn("Time's up. Quitting now...(it may take a while)\\n") + quit = 1 + } +} +_EOC_ + } + +} else { + # in kernel-space only + $stap_src = <<_EOC_; +$preamble + +global bts; + +probe timer.profile { + if ($condition && !user_mode()) { + bts[backtrace()] <<< 1 + } +} + +probe end { + nstacks = 0 + foreach (bt in bts limit 1) { + nstacks++ + } + + if (nstacks == 0) { + warn("No backtraces found. Quitting now...\\n") + + } else { + foreach (bt in bts- limit $limit) { + print_stack(bt) + printf("\\t%d\\n", \@count(bts[bt])) + } + } +} + +probe timer.s($time) { + warn("Time's up. Quitting now...(it may take a while)\\n") + exit() +} +_EOC_ +} + +if ($opts{d}) { + print $stap_src; + exit; +} + +my %so_files; +{ + my $maps_file = "/proc/$pid/maps"; + open my $in, $maps_file + or die "Cannot open $maps_file for reading: $!\n"; + + while (<$in>) { + if (/\s+(\/\S+\.so(?:\.\S+)?)$/) { + if (!exists $so_files{$1}) { + $so_files{$1} = 1; + #warn $1, "\n"; + } + } + } + + close $in; +} + +my $d_so_args; +if (%so_files) { + $d_so_args = join " ", map { "-d $_" } sort keys %so_files; + +} else { + $d_so_args = ''; +} + +open my $in, "|stap --skip-badvars --all-modules -x $pid -d '$nginx_path' --ldd $d_so_args $stap_args -" + or die "Cannot run stap: $!\n"; + +print $in $stap_src; + +close $in; + +sub usage { + return <<'_EOC_'; +Usage: + sample-bt [optoins] + +Options: + -a Pass extra arguments to the stap utility. + -d Dump out the systemtap script source. + -h Print this usage. + -l Only output most frenquent backtrace samples. + (Default to 1024) + -p Specify the user process pid. + -t Specify the number of seconds for sampling. + -u Sample in the user-space. + -k Sample in the kernel-space. + +Examples: + sample-bt -p 12345 -t 10 + sample-bt -p 12345 -t 5 -a '-DMAXACTION=100000' +_EOC_ +} diff --git a/stap/sample-bt-off-cpu b/stap/sample-bt-off-cpu new file mode 100644 index 0000000..c342f3f --- /dev/null +++ b/stap/sample-bt-off-cpu @@ -0,0 +1,308 @@ +#!/usr/bin/env perl + +# Copyright (C) Yichun Zhang (agentzh) + +# Thanks Brendan Gregg for the inspiration given here: +# http://dtrace.org/blogs/brendan/2011/07/08/off-cpu-performance-analysis/ + +use 5.006001; +use strict; +use warnings; + +use Getopt::Long qw( GetOptions ); + +GetOptions("a=s", \(my $stap_args), + "d", \(my $dump_src), + "distr", \(my $check_distr), + "h", \(my $help), + "k", \(my $check_kernel), + "l=i", \(my $limit), + "min=i", \(my $min_elapsed), + "p=i", \(my $pid), + "t=i", \(my $time), + "u", \(my $check_user)) + or die usage(); + +if ($help) { + print usage(); + exit; +} + +if (!defined $min_elapsed) { + $min_elapsed = 4; +} + +if (!defined $pid) { + die "No process pid specified by the -p option.\n"; +} + +my $condition = "pid() == target()"; + +if (!defined $time) { + die "No -t option specified.\n"; +} + +if (!defined $limit) { + $limit = 1024; +} + +if (!defined $stap_args) { + $stap_args = ''; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXACTION=/) { + $stap_args .= " -DMAXACTION=100000"; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXMAPENTRIES=/) { + $stap_args .= " -DMAXMAPENTRIES=3000"; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXBACKTRACE=/) { + $stap_args .= " -DMAXBACKTRACE=200"; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXSTRINGLEN=2048/) { + $stap_args .= " -DMAXSTRINGLEN=2048"; +} + +$stap_args .= " -DSTP_NO_OVERLOAD"; + +if ($^O ne 'linux') { + die "Only linux is supported but I am on $^O.\n"; +} + +my $exec_file = "/proc/$pid/exe"; +if (!-f $exec_file) { + die "User process $pid is not running or ", + "you do not have enough permissions.\n"; +} + +my $exec_path = readlink $exec_file; + +my $ver = `stap --version 2>&1`; +if (!defined $ver) { + die "Systemtap not installed or its \"stap\" utility is not visible to the PATH environment: $!\n"; +} + +if ($ver =~ /version\s+(\d+\.\d+)/i) { + my $v = $1; + if ($v < 2.1) { + die "ERROR: at least systemtap 2.1 is required but found $v\n"; + } + +} else { + die "ERROR: unknown version of systemtap:\n$ver\n"; +} + +my $preamble = <<_EOC_; +global quit = 0; +global found + +probe begin { + warn(sprintf("Tracing %d ($exec_path)...\\n", target())) +} +_EOC_ + +my $postamble = <<_EOC_; +probe timer.s($time) { + if (!found) { + warn("No backtraces found. Quitting now...\\n") + exit() + + } else { + warn("Time's up. Quitting now...(it may take a while)\\n") + quit = 1 + } +} +_EOC_ + +my $stap_src; +my $d_so_args = ''; + +if ($check_distr) { + $stap_src = <<_EOC_; +global start_time +global elapsed_times + +$preamble + +probe end { + if (!found) { + println("\\nNo samples found yet.") + + } else { + println("=== Off-CPU time distribution (in us) ===") + printf("min/avg/max: %d/%d/%d\\n", + \@min(elapsed_times), \@avg(elapsed_times), \@max(elapsed_times)) + println(\@hist_log(elapsed_times)) + } +} + +probe scheduler.cpu_off { + if ($condition) { + if (!quit) { + start_time[tid()] = gettimeofday_us() + + } else { + exit() + } + } +} + +probe scheduler.cpu_on { + if ($condition && !quit) { + t = tid() + begin = start_time[t] + if (begin > 0) { + elapsed = gettimeofday_us() - begin + if (elapsed >= $min_elapsed) { + found = 1 + elapsed_times <<< elapsed + } + delete start_time[t] + } + } +} + +probe timer.s($time) { + println("Exiting...Please wait...") + quit = 1 +} +_EOC_ + +} else { + # for sampling backtraces + + my ($gen_bt_code, $print_bt_code); + + if ($check_kernel) { + if ($check_user) { + $gen_bt_code = "backtrace(), ubacktrace()"; + $print_bt_code = <<_EOC_; + foreach ([kbt, ubt] in bts- limit $limit) { + print_stack(kbt) + print_ustack(ubt) + printf("\\t%d\\n", \@sum(bts[kbt, ubt])) + } +_EOC_ + + } else { + $gen_bt_code = "backtrace()"; + $print_bt_code = <<_EOC_; + foreach (bt in bts- limit $limit) { + print_stack(bt) + printf("\\t%d\\n", \@sum(bts[bt])) + } +_EOC_ + } + + } else { + $gen_bt_code = "ubacktrace()"; + $print_bt_code = <<_EOC_; + foreach (bt in bts- limit $limit) { + print_ustack(bt) + printf("\\t%d\\n", \@sum(bts[bt])) + } +_EOC_ + } + + $stap_src = <<_EOC_; +global bts +global start_time + +$preamble + +probe scheduler.cpu_off { + if ($condition) { + if (!quit) { + start_time[tid()] = gettimeofday_us() + + } else { +$print_bt_code + exit() + } + } +} + +probe scheduler.cpu_on { + if ($condition && !quit) { + t = tid() + begin = start_time[t] + if (begin > 0) { + elapsed = gettimeofday_us() - begin + if (elapsed >= $min_elapsed) { + bts[$gen_bt_code] <<< elapsed + found = 1 + } + delete start_time[t] + } + } +} + +$postamble +_EOC_ + + my %so_files; + { + my $maps_file = "/proc/$pid/maps"; + open my $in, $maps_file + or die "Cannot open $maps_file for reading: $!\n"; + + while (<$in>) { + if (/\s+(\/\S+\.so(?:\.\S+)?)$/) { + if (!exists $so_files{$1}) { + $so_files{$1} = 1; + #warn $1, "\n"; + } + } + } + + close $in; + } + + if (%so_files) { + $d_so_args = join " ", map { "-d $_" } sort keys %so_files; + } +} + +if ($dump_src) { + print $stap_src; + exit; +} + + +#warn "$d_so_args\n"; + +open my $in, "|stap --skip-badvars --all-modules -x $pid -d '$exec_path' --ldd $d_so_args $stap_args -" + or die "Cannot run stap: $!\n"; + +print $in $stap_src; + +close $in; + +sub usage { + return <<'_EOC_'; +Usage: + sample-bt-off-cpu [optoins] + +Options: + -a Pass extra arguments to the stap utility. + -d Dump out the systemtap script source. + --distr Analyze the distribution of the elapsed off-CPU times only. + -h Print this usage. + -k Analyze kernelspace backtraces. + -l Only output most frenquent backtrace samples. + (Default to 1024) + --min= Minimal elapsed off-CPU time to be tracked. + (Default to 4us) + -p Specify the user process pid. + -t Specify the number of seconds for sampling. + -u Analyze userspace backtraces. + +Examples: + sample-bt-off-cpu -p 12345 -t 10 + sample-bt-off-cpu -p 12345 -t 5 -a '-DMAXACTION=100000' + sample-bt-off-cpu --distr -p 12345 -t 10 --min=1 +_EOC_ +} diff --git a/stap/sample-bt-vfs b/stap/sample-bt-vfs new file mode 100644 index 0000000..d77b222 --- /dev/null +++ b/stap/sample-bt-vfs @@ -0,0 +1,233 @@ +#!/usr/bin/env perl + +# Copyright (C) Yichun Zhang (agentzh) + +use 5.006001; +use strict; +use warnings; + +use Getopt::Long qw( GetOptions ); + +GetOptions("a=s", \(my $stap_args), + "d", \(my $dump_src), + "h", \(my $help), + "l=i", \(my $limit), + "latency", \(my $check_latency), + "p=i", \(my $pid), + "r", \(my $readonly), + "w", \(my $writeonly), + "t=i", \(my $time)) + or die usage(); + +if ($help) { + print usage(); + exit; +} + +if (!defined $pid) { + die "No process pid specified by the -p option.\n"; +} + +my $condition = "pid() == target()"; + +if (!defined $time) { + die "No -t option specified.\n"; +} + +if (!defined $limit) { + $limit = 1024; +} + +if (!defined $stap_args) { + $stap_args = ''; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXACTION=/) { + $stap_args .= " -DMAXACTION=100000"; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXMAPENTRIES=/) { + $stap_args .= " -DMAXMAPENTRIES=5000"; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXBACKTRACE=/) { + $stap_args .= " -DMAXBACKTRACE=200"; +} + +if ($stap_args !~ /(?:^|\s)-D\s*MAXSTRINGLEN=2048/) { + $stap_args .= " -DMAXSTRINGLEN=2048"; +} + +$stap_args .= " -DSTP_NO_OVERLOAD"; + +if ($^O ne 'linux') { + die "Only linux is supported but I am on $^O.\n"; +} + +my $exec_file = "/proc/$pid/exe"; +if (!-f $exec_file) { + die "Nginx process $pid is not running or ", + "you do not have enough permissions.\n"; +} + +my $exec_path = readlink $exec_file; + +my $ver = `stap --version 2>&1`; +if (!defined $ver) { + die "Systemtap not installed or its \"stap\" utility is not visible to the PATH environment: $!\n"; +} + +if ($ver =~ /version\s+(\d+\.\d+)/i) { + my $v = $1; + if ($v < 2.1) { + die "ERROR: at least systemtap 2.1 is required but found $v\n"; + } + +} else { + die "ERROR: unknown version of systemtap:\n$ver\n"; +} + +my $preamble = <<_EOC_; +global bts; +global quit = 0; + +probe begin { + warn(sprintf("Tracing %d ($exec_path)...\\n", target())) +} +_EOC_ + +my $postamble = <<_EOC_; +probe timer.s($time) { + nstacks = 0 + foreach (bt in bts limit 10) { + nstacks++ + } + + if (nstacks == 0) { + warn(sprintf("Too few backtraces (%d) found. Quitting now...\\n", nstacks)) + exit() + + } else { + warn("Time's up. Quitting now...(it may take a while)\\n") + quit = 1 + } +} +_EOC_ + +my @probes; +if ($readonly) { + @probes = qw(vfs.read); + +} elsif ($writeonly) { + @probes = qw(vfs.write); + +} else { + @probes = qw(vfs.read vfs.write); +} + +my $return_probes = join ", ", map { "$_.return" } @probes; + +my $stap_src; +if ($check_latency) { + # analyze I/O latency + my $entry_probes = join ", ", @probes; + $stap_src = <<_EOC_; +$preamble + +global start_time + +probe $entry_probes { + if ($condition) { + if (!quit) { + if (devname != "N/A") { + start_time = gettimeofday_us() + } + + } else { + foreach (bt in bts- limit $limit) { + print_ustack(bt) + printf("\\t%d\\n", \@sum(bts[bt])) + } + + exit() + } + } +} + +probe $return_probes { + if ($condition) { + if (!quit) { + if (\$return > 0 && devname != "N/A" && start_time) { + bts[ubacktrace()] <<< gettimeofday_us() - start_time + start_time = 0; + } + } + } +} + +$postamble +_EOC_ + +} else { + # analyze I/O data volumn + $stap_src = <<_EOC_; +$preamble + +probe $return_probes { + if ($condition) { + if (!quit) { + if (\$return > 0 && devname != "N/A") { + bts[ubacktrace()] <<< \$return + } + + } else { + + foreach (bt in bts- limit $limit) { + print_ustack(bt); + printf("\\t%d\\n", \@sum(bts[bt])) + } + + exit() + } + } +} + +$postamble +_EOC_ +} + +if ($dump_src) { + print $stap_src; + exit; +} + +open my $in, "|stap --skip-badvars --all-modules -x $pid -d '$exec_path' --ldd $stap_args -" + or die "Cannot run stap: $!\n"; + +print $in $stap_src; + +close $in; + +sub usage { + return <<'_EOC_'; +Usage: + sample-bt-vfs [optoins] + +Options: + -a Pass extra arguments to the stap utility. + -d Dump out the systemtap script source. + -h Print this usage. + --latency Analyze the VFS kernel calls' latency instead + of I/O data volumn. + -l Only output most frenquent backtrace samples. + (Default to 1024) + -p Specify the user process pid. + -r Probe file reading operations only. + -w Probe file writing operations only. + -t Specify the number of seconds for sampling. + +Examples: + sample-bt-vfs -p 12345 -t 10 + sample-bt-vfs -p 12345 -t 5 -a '-DMAXACTION=100000' +_EOC_ +} diff --git a/wiki/Design.md b/wiki/Design.md new file mode 100644 index 0000000..d9660f3 --- /dev/null +++ b/wiki/Design.md @@ -0,0 +1,12 @@ +# node-lua设计 +##引擎架构 +##消息转发 +1. 多线程 任务多路复用 +2. 线程队列 +3. 优化work-stealing +##服务管理,context设计 +##网络层设计 +##同步、异步底层设计,coroutine管理,初始化设计 +##tcp,udp设计 +##timer设计 +##buffer设计 diff --git a/wiki/Home.md b/wiki/Home.md new file mode 100644 index 0000000..d88049d --- /dev/null +++ b/wiki/Home.md @@ -0,0 +1,27 @@ +Welcome to the node-lua wiki! +## node-lua简介 +node-lua是一款基于lua实现的脚本和服务器引擎,它支持构建海量lua服务(context_lua)并以多线程方式运行在多核服务器上,采用了任务多路复用的设计方案,有效利用了多核优势。node-lua致力于构建一个快速、简单易用的lua脚本和服务器开发和运行环境。该引擎参考了node-js和skynet的设计思想,并对其进行了整合和优化。 + +## 该引擎当前版本实现了以下特性: +1. 引擎核心层同时支持同步阻塞和异步回调的api设计方案,让异步io等接口更加简单易用。调用同步和异步api时引擎核心层将会自动挂起正在执行的lua函数(coroutine),直接放弃占用的cpu资源; +2. 服务创建以指定lua文件为入口脚本文件,脚本直接运行在新服务的coroutine环境中,支持在入口脚本当中直接调用同步和异步api接口,支持无限循环调用同步api接口; +3. 可以创建海量独立的lua服务(context_lua),引擎会根据当前运行服务总量和物理核心数量动态调整工作线程数量,当引擎没有需要处理的服务请求时(即任务队列为空),所有物理线程将会挂起等待,直到有新的任务(lua同步和异步回调)需要被唤醒运行; +4. 支持在用户创建的lua coroutine中直接调用引擎提供的同步和异步api接口,对用户态coroutine执行不会产生任何影响; +5. 引擎会检测进程当中有效的lua服务总量,当服务总量为0时,引擎会自动安全退出(可以用node-lua作为简单的lua脚本解释器使用)。同时,lua服务也会检测服务当中运行和挂起的同步和异步回调总量,当回调总量为0时,lua服务会被标记并安全退出; +6. 引擎框架任务调度采用了线程任务队列的设计,减少了不同线程之间对任务资源的竞争。另外,任务调度也引入了work-stealing算法对调度进行了深度优化; +7. 引擎提供了tcp socket相关的大部分api接口,同时支持unix_domain_socket; +8. 引擎提供了lua服务相关的api接口,包括服务创建、销毁、通信等同步和异步接口; +9. 引擎提供了定时相关的api接口,基本满足所有的定时设计,并直接被嵌入到了其它同步和异步api接口当中(tcp,服务相关的api等); +10. 引擎提供了快速lua字符串缓存结构,可以用于高效的广播接口当中; +11. 引擎当前提供了centos,macos,windows环境下的编译和安装; + +## node-lua在下个版本将会提供以下新功能和特性: +1. udp接口支持; +2. tcp和udp socket在lua服务之间的迁移; +3. 在线服务热更新; +4. 在线服务调试; +5. 提供FreeBSD等环境下的编译和安装; +6. 提供基准测试版本; + +## 写在最后 +欢迎大家发现node-lua当中的bug,对node-lua提出更好的建议! \ No newline at end of file