diff options
Diffstat (limited to 'src/copy.inc')
-rw-r--r-- | src/copy.inc | 170 |
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 | |||
26 | typedef 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 | |||
34 | static 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 | |||
64 | static 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 | |||
141 | static 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 | } | ||