aboutsummaryrefslogtreecommitdiff
path: root/src/copy.inc
diff options
context:
space:
mode:
Diffstat (limited to 'src/copy.inc')
-rw-r--r--src/copy.inc170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/copy.inc b/src/copy.inc
new file mode 100644
index 0000000..21ba261
--- /dev/null
+++ b/src/copy.inc
@@ -0,0 +1,170 @@
1/******************************************************************************
2* Copyright (c) 2011 by Robert G. Jakabosky <bobby@sharedrealm.com>
3*
4* Permission is hereby granted, free of charge, to any person obtaining a copy
5* of this software and associated documentation files (the "Software"), to deal
6* in the Software without restriction, including without limitation the rights
7* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8* copies of the Software, and to permit persons to whom the Software is
9* furnished to do so, subject to the following conditions:
10*
11* The above copyright notice and this permission notice shall be included in
12* all copies or substantial portions of the Software.
13*
14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20* THE SOFTWARE.
21******************************************************************************/
22
23/* maximum recursive depth of table copies. */
24#define MAX_COPY_DEPTH 30
25
26typedef struct {
27 lua_State *from_L;
28 lua_State *to_L;
29 int has_cache;
30 int cache_idx;
31 int is_arg;
32} llthread_copy_state;
33
34static int llthread_copy_table_from_cache(llthread_copy_state *state, int idx) {
35 void *ptr;
36
37 /* convert table to pointer for lookup in cache. */
38 ptr = (void *)lua_topointer(state->from_L, idx);
39 if(ptr == NULL) return 0; /* can't convert to pointer. */
40
41 /* check if we need to create the cache. */
42 if(!state->has_cache) {
43 lua_newtable(state->to_L);
44 lua_replace(state->to_L, state->cache_idx);
45 state->has_cache = 1;
46 }
47
48 lua_pushlightuserdata(state->to_L, ptr);
49 lua_rawget(state->to_L, state->cache_idx);
50 if(lua_isnil(state->to_L, -1)) {
51 /* not in cache. */
52 lua_pop(state->to_L, 1);
53 /* create new table and add to cache. */
54 lua_newtable(state->to_L);
55 lua_pushlightuserdata(state->to_L, ptr);
56 lua_pushvalue(state->to_L, -2);
57 lua_rawset(state->to_L, state->cache_idx);
58 return 0;
59 }
60 /* found table in cache. */
61 return 1;
62}
63
64static int llthread_copy_value(llthread_copy_state *state, int depth, int idx) {
65 const char *str;
66 size_t str_len;
67 int kv_pos;
68
69 /* Maximum recursive depth */
70 if(++depth > MAX_COPY_DEPTH) {
71 return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH);
72 }
73
74 /* only support string/number/boolean/nil/table/lightuserdata. */
75 switch(lua_type(state->from_L, idx)) {
76 case LUA_TNIL:
77 lua_pushnil(state->to_L);
78 break;
79 case LUA_TNUMBER:
80 lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx));
81 break;
82 case LUA_TBOOLEAN:
83 lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx));
84 break;
85 case LUA_TSTRING:
86 str = lua_tolstring(state->from_L, idx, &(str_len));
87 lua_pushlstring(state->to_L, str, str_len);
88 break;
89 case LUA_TLIGHTUSERDATA:
90 lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx));
91 break;
92 case LUA_TTABLE:
93 /* make sure there is room on the new state for 3 values (table,key,value) */
94 if(!lua_checkstack(state->to_L, 3)) {
95 return luaL_error(state->from_L, "To stack overflow!");
96 }
97 /* make room on from stack for key/value pairs. */
98 luaL_checkstack(state->from_L, 2, "From stack overflow!");
99
100 /* check cache for table. */
101 if(llthread_copy_table_from_cache(state, idx)) {
102 /* found in cache don't need to copy table. */
103 break;
104 }
105 lua_pushnil(state->from_L);
106 while (lua_next(state->from_L, idx) != 0) {
107 /* key is at (top - 1), value at (top), but we need to normalize these
108 * to positive indices */
109 kv_pos = lua_gettop(state->from_L);
110 /* copy key */
111 llthread_copy_value(state, depth, kv_pos - 1);
112 /* copy value */
113 llthread_copy_value(state, depth, kv_pos);
114 /* Copied key and value are now at -2 and -1 in state->to_L. */
115 lua_settable(state->to_L, -3);
116 /* Pop value for next iteration */
117 lua_pop(state->from_L, 1);
118 }
119 break;
120 case LUA_TFUNCTION:
121 if(lua_iscfunction(state->from_L, idx)){
122 lua_CFunction fn = lua_tocfunction(state->from_L, idx);
123 lua_pushcfunction(state->to_L, fn);
124 break;
125 }
126 case LUA_TUSERDATA:
127 case LUA_TTHREAD:
128 default:
129 if (state->is_arg) {
130 return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported.");
131 } else {
132 /* convert un-supported types to an error string. */
133 lua_pushfstring(state->to_L, "Un-supported value: %s: %p",
134 lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx));
135 }
136 }
137
138 return 1;
139}
140
141static int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg) {
142 llthread_copy_state state;
143 int nvalues = 0;
144 int n;
145
146 nvalues = (top - idx) + 1;
147 /* make sure there is room on the new state for the values. */
148 if(!lua_checkstack(to_L, nvalues + 1)) {
149 return luaL_error(from_L, "To stack overflow!");
150 }
151
152 /* setup copy state. */
153 state.from_L = from_L;
154 state.to_L = to_L;
155 state.is_arg = is_arg;
156 state.has_cache = 0; /* don't create cache table unless it is needed. */
157 lua_pushnil(to_L);
158 state.cache_idx = lua_gettop(to_L);
159
160 nvalues = 0;
161 for(n = idx; n <= top; n++) {
162 llthread_copy_value(&state, 0, n);
163 ++nvalues;
164 }
165
166 /* remove cache table. */
167 lua_remove(to_L, state.cache_idx);
168
169 return nvalues;
170}