/* TODO: typed object support */ static void amf0_encode_table_as_object(amf_buf *buf, lua_State *L, int idx, int ridx) { size_t key_len; const char *key; amf_buf_append_char(buf, AMF0_OBJECT); for (lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) { switch (lua_type(L, -2)) { case LUA_TNUMBER: lua_pushvalue(L, -2); key = lua_tolstring(L, -1, &key_len); amf_buf_append_u16(buf, (uint16_t)key_len); amf_buf_append(buf, key, key_len); lua_pop(L, 1); break; case LUA_TSTRING: key = lua_tolstring(L, -2, &key_len); amf_buf_append_u16(buf, (uint16_t)key_len); amf_buf_append(buf, key, key_len); break; default: continue; } amf0_encode(L, buf, 0, -1, ridx); } amf_buf_append_u16(buf, (uint16_t)0); amf_buf_append_char(buf, AMF0_END_OF_OBJECT); }
static int amf0_encode_ref(amf_buf *buf, lua_State *L, int idx, int ridx) { int ref; /* lookup in the ref table */ lua_pushvalue(L, idx); lua_rawget(L, ridx); ref = (lua_isnumber(L, -1) ? lua_tonumber(L, -1) : -1); lua_pop(L, 1); if (ref >= 0) { amf_buf_append_char(buf, AMF0_REFERENCE); if (ref > UINT16_MAX) { luaL_error(L, "amf0 reference overflow"); } amf_buf_append_u16(buf, ref); } else { save_ref(L, idx, ridx); } return ref; }
static void amf3_encode_table_as_object(lua_State *L, amf_buf *buf, int idx, int sidx, int oidx, int tidx) { abs_idx(L, idx); amf_buf_append_char(buf, AMF3_OBJECT); if (amf3_encode_ref(L, buf, idx, oidx) >= 0) { return; } lua_newtable(L); /* traits table */ int members = 1; for(lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) { switch (lua_type(L, -2)) { case LUA_TNUMBER: lua_pushvalue(L, -2); lua_tostring(L, -1); break; case LUA_TSTRING: lua_pushvalue(L, -2); break; default: continue; } lua_rawseti(L, -4, members++); } amf3_encode_traits(L, buf, lua_gettop(L), sidx, tidx); lua_pop(L, 1); /* drop the traits table */ for(lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) { amf3_encode(L, buf, -1, sidx, oidx, tidx); } }
static void encode_hdr(lua_State *L, amf_buf *buf, int ver) { if(!lua_istable(L, -1)) { luaL_error(L, "invalid header structure, must be a dense table"); } /* name */ lua_rawgeti(L, -1, 1); size_t len; const char *name = lua_tolstring(L, -1, &len); amf_buf_append_u16(buf, (uint16_t)len); if (len > 0) amf_buf_append(buf, name, (uint16_t)len); lua_pop(L, 1); /* must understand */ lua_rawgeti(L, -1, 2); int mu = lua_toboolean(L, -1); amf_buf_append_char(buf, (char)mu); lua_pop(L, 1); /* content length */ amf_buf_append_u32(buf, (uint32_t)0); lua_rawgeti(L, -1, 3); lua_newtable(L); amf0_encode(L, buf, (ver == 3), -2, lua_gettop(L)); lua_pop(L, 2); /* pops the obj and ref table */ }
static int lua_amf_buffer_write_uchar(lua_State *L) { amf_buf *b = luaL_checkudata(L, 1, "amf_buffer"); unsigned char c = (unsigned char)luaL_checkint(L, 2); amf_buf_append_char(b, c); return 0; }
static void amf0_encode_string(amf_buf *b, const char *s, size_t len) { uint16_t u16; uint32_t u32; if (len < UINT16_MAX) { u16 = (uint16_t)len; amf_buf_append_char(b, AMF0_STRING); amf_buf_append_u16(b, u16); amf_buf_append(b, s, u16); } else { // long string if (len > UINT32_MAX) { len = UINT32_MAX; } u32 = (uint32_t)len; amf_buf_append_char(b, AMF0_L_STRING); amf_buf_append_u32(b, u32); amf_buf_append(b, s, u32); } }
static void amf0_encode_table_as_array(amf_buf *buf, lua_State *L, int idx, int ridx, int len) { amf_buf_append_char(buf, AMF0_STRICT_ARRAY); amf_buf_append_u32(buf, (uint32_t)len); // array count for (int i = 1; i <= len; i++) { lua_pushinteger(L, i); lua_gettable(L, idx); amf0_encode(L, buf, 0, -1, ridx); lua_pop(L, 1); } }
static void amf3_encode_table_as_array(lua_State *L, amf_buf *buf, int idx, int sidx, int oidx, int tidx, int array_len) { abs_idx(L, idx); amf_buf_append_char(buf, AMF3_ARRAY); if (amf3_encode_ref(L, buf, idx, oidx) >=0) { return; } amf_buf_append_u29(buf, (array_len << 1) | 1); /*Send an empty string to imply no named keys*/ amf_buf_append_u29(buf, (0 << 1) | 1); for (int i = 1; i <= array_len; i++) { lua_rawgeti(L, idx, i); amf3_encode(L, buf, -1, sidx, oidx, tidx); lua_pop(L, 1); } }
void amf3_encode(lua_State *L, amf_buf *buf, int idx, int sidx, int oidx, int tidx) { int old_top, array_len; const char *str; size_t str_len; old_top = lua_gettop(L); switch (lua_type(L, idx)) { case LUA_TNIL: { amf_buf_append_char(buf, AMF3_NULL); break; } case LUA_TBOOLEAN: { if (lua_toboolean(L, idx)) { amf_buf_append_char(buf, AMF3_TRUE); } else { amf_buf_append_char(buf, AMF3_FALSE); } break; } case LUA_TNUMBER: { lua_Number n = lua_tonumber(L, idx); /* encode as double */ if (floor(n) != n || n < AMF3_MIN_INT || n > AMF3_MAX_INT) { amf_buf_append_char(buf, AMF3_DOUBLE); amf_buf_append_double(buf, n); } else { amf_buf_append_char(buf, AMF3_INTEGER); amf_buf_append_u29(buf, (int)n); } break; } case LUA_TSTRING: { str = lua_tolstring(L, idx, &str_len); amf_buf_append_char(buf, AMF3_STRING); amf3_encode_string(L, buf, idx, sidx); break; } case LUA_TTABLE: array_len = strict_array_length(L, idx); if (array_len == 0) { amf_buf_append_char(buf, AMF3_NULL); } else if (array_len > 0) { amf3_encode_table_as_array(L, buf, idx, sidx, oidx, tidx, array_len); } else { amf3_encode_table_as_object(L, buf, idx, sidx, oidx, tidx); } } assert(lua_gettop(L) == old_top); }
void amf0_encode(lua_State *L, amf_buf *buf, int avmplus, int idx, int ridx) { int array_len, old_top, ref; size_t len; const char *str; abs_idx(L, idx); abs_idx(L, ridx); old_top = lua_gettop(L); switch (lua_type(L, idx)) { case LUA_TNIL: amf_buf_append_char(buf, AMF0_NULL); break; case LUA_TBOOLEAN: amf_buf_append_char(buf, AMF0_BOOLEAN); amf_buf_append_char(buf, lua_toboolean(L, idx) ? 1 : 0); break; case LUA_TNUMBER: amf_buf_append_char(buf, AMF0_NUMBER); amf0_encode_number(buf, lua_tonumber(L, idx)); break; case LUA_TSTRING: str = lua_tolstring(L, idx, &len); amf0_encode_string(buf, str, len); break; case LUA_TTABLE: { if (avmplus) { lua_newtable(L); lua_newtable(L); lua_newtable(L); int top = lua_gettop(L); amf3_encode(L, buf, idx, top-2, top-1, top); lua_pop(L, 3); } else { ref = amf0_encode_ref(buf, L, idx, ridx); if (ref >= 0) { break; } array_len = strict_array_length(L, idx); if (array_len == 0) { amf_buf_append_char(buf, AMF0_NULL); } else if (array_len > 0) { amf0_encode_table_as_array(buf, L, idx, ridx, array_len); } else { amf0_encode_table_as_object(buf, L, idx, ridx); } } break; } } assert(lua_gettop(L) == old_top); }