From 6556cc558f0602cc99b1a8d1c7212b2e91490cdc Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Wed, 20 Mar 2024 16:23:54 +0100 Subject: C++ migration: UniqueKey --- src/cancel.h | 6 +++--- src/deep.cpp | 6 +++--- src/deep.h | 11 +++++------ src/keeper.cpp | 6 +++--- src/keeper.h | 2 +- src/lanes.cpp | 25 +++++++++++++------------ src/linda.cpp | 18 +++++++++--------- src/macros_and_utils.h | 4 ++-- src/tools.cpp | 12 ++++++------ src/tools.h | 4 ++-- src/uniquekey.h | 43 +++++++++++++++++++++++++++++++------------ src/universe.cpp | 2 +- src/universe.h | 3 +-- 13 files changed, 80 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/cancel.h b/src/cancel.h index e5dfe23..c1bee4e 100644 --- a/src/cancel.h +++ b/src/cancel.h @@ -46,17 +46,17 @@ typedef enum } CancelOp; // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel +static constexpr UniqueKey CANCEL_ERROR{ 0xe97d41626cc97577ull }; // 'cancel_error' sentinel // crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key +static constexpr UniqueKey CANCEL_TEST_KEY{ 0xe66f5960c57d133aull }; // used as registry key cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool force_, double waitkill_timeout_); static inline int cancel_error( lua_State* L) { STACK_GROW( L, 1); - push_unique_key( L, CANCEL_ERROR); // special error value + CANCEL_ERROR.push(L); // special error value return lua_error( L); // doesn't return } diff --git a/src/deep.cpp b/src/deep.cpp index 897dc63..c0aa25b 100644 --- a/src/deep.cpp +++ b/src/deep.cpp @@ -59,13 +59,13 @@ THE SOFTWARE. * idfunc -> metatable */ // crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( DEEP_LOOKUP_KEY, 0x9fb9b4f3f633d83d); +static constexpr UniqueKey DEEP_LOOKUP_KEY{ 0x9fb9b4f3f633d83dull }; /* * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying * crc64/we of string "DEEP_PROXY_CACHE_KEY" generated at http://www.nitrxgen.net/hashgen/ */ -static DECLARE_CONST_UNIQUE_KEY( DEEP_PROXY_CACHE_KEY, 0x05773d6fc26be106); +static constexpr UniqueKey DEEP_PROXY_CACHE_KEY{ 0x05773d6fc26be106ull }; /* * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. @@ -390,7 +390,7 @@ int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc, int nuv_) { return luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)"); } - if( prelude->magic.value != DEEP_VERSION.value) + if( prelude->magic != DEEP_VERSION) { // just in case, don't leak the newly allocated deep userdata object lua_pushlightuserdata( L, prelude); diff --git a/src/deep.h b/src/deep.h index 728aa2a..d8a1772 100644 --- a/src/deep.h +++ b/src/deep.h @@ -42,18 +42,17 @@ typedef void* (*luaG_IdFunction)( lua_State* L, DeepOp op_); // ################################################################################################ // fnv164 of string "DEEP_VERSION_2" generated at https://www.pelock.com/products/hash-calculator -static DECLARE_CONST_UNIQUE_KEY( DEEP_VERSION, 0xB4B0119C10642B29); +static constexpr UniqueKey DEEP_VERSION{ 0xB4B0119C10642B29ull }; // should be used as header for full userdata -struct s_DeepPrelude +struct DeepPrelude { - DECLARE_UNIQUE_KEY( magic); // must be filled by the Deep userdata idfunc that allocates it on eDO_new operation + UniqueKey const magic{ DEEP_VERSION }; // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc - luaG_IdFunction idfunc; + luaG_IdFunction idfunc { nullptr }; // data is destroyed when refcount is 0 - volatile int refcount; + volatile int refcount{ 0 }; }; -typedef struct s_DeepPrelude DeepPrelude; char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, int nuv_, LookupMode mode_); void free_deep_prelude( lua_State* L, DeepPrelude* prelude_); diff --git a/src/keeper.cpp b/src/keeper.cpp index 1e344f2..697fe71 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp @@ -166,7 +166,7 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) // in: linda_ud expected at *absolute* stack slot idx // out: fifos[ud] // crc64/we of string "FIFOS_KEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); +static constexpr UniqueKey FIFOS_KEY{ 0xdce50bbc351cd465ull }; static void push_table( lua_State* L, int idx_) { STACK_GROW( L, 4); @@ -769,13 +769,13 @@ void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mod { if( lua_isnil( L, i)) { - push_unique_key( L, NIL_SENTINEL); + NIL_SENTINEL.push(L); lua_replace( L, i); } } else { - if( equal_unique_key( L, i, NIL_SENTINEL)) + if (NIL_SENTINEL.equals(L, i)) { lua_pushnil( L); lua_replace( L, i); diff --git a/src/keeper.h b/src/keeper.h index 33118be..4cf3605 100644 --- a/src/keeper.h +++ b/src/keeper.h @@ -43,7 +43,7 @@ void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mod int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_); // crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( NIL_SENTINEL, 0x7eaafa003a1d11a1); +static constexpr UniqueKey NIL_SENTINEL{ 0x7eaafa003a1d11a1ull }; typedef lua_CFunction keeper_api_t; #define KEEPER_API( _op) keepercall_ ## _op diff --git a/src/lanes.cpp b/src/lanes.cpp index fa69656..126f65c 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp @@ -128,7 +128,7 @@ static void securize_debug_threadname( lua_State* L, Lane* s) #if ERROR_FULL_STACK static int lane_error( lua_State* L); // crc64/we of string "STACKTRACE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( STACKTRACE_REGKEY, 0x534af7d3226a429f); +static constexpr UniqueKey STACKTRACE_REGKEY{ 0x534af7d3226a429full }; #endif // ERROR_FULL_STACK /* @@ -140,7 +140,7 @@ static DECLARE_CONST_UNIQUE_KEY( STACKTRACE_REGKEY, 0x534af7d3226a429f); * anyways complicate that approach. */ // crc64/we of string "FINALIZER_REGKEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( FINALIZER_REGKEY, 0x188fccb8bf348e09); +static constexpr UniqueKey FINALIZER_REGKEY{ 0x188fccb8bf348e09ull }; struct s_Linda; @@ -649,7 +649,7 @@ LUAG_FUNC( set_singlethreaded) #if ERROR_FULL_STACK // crc64/we of string "EXTENDED_STACKTRACE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( EXTENDED_STACKTRACE_REGKEY, 0x2357c69a7c92c936); // used as registry key +static constexpr UniqueKey EXTENDED_STACKTRACE_REGKEY{ 0x2357c69a7c92c936ull }; // used as registry key LUAG_FUNC( set_error_reporting) { @@ -683,7 +683,7 @@ static int lane_error( lua_State* L) // Don't do stack survey for cancelled lanes. // - if( equal_unique_key( L, 1, CANCEL_ERROR)) + if (CANCEL_ERROR.equals(L, 1)) { return 1; // just pass on } @@ -769,7 +769,7 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_) // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed // For other errors, the message can be whatever was thrown, and we should have a stack trace table - ASSERT_L( lua_type( L, 1 + stk_base_) == (equal_unique_key( L, stk_base_, CANCEL_ERROR) ? LUA_TNIL : LUA_TTABLE)); + ASSERT_L(lua_type(L, 1 + stk_base_) == (CANCEL_ERROR.equals(L, stk_base_) ? LUA_TNIL : LUA_TTABLE)); // Just leaving the stack trace table on the stack is enough to get it through to the master. break; } @@ -779,14 +779,15 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_) case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) default: // we should have a single value which is either a string (the error message) or CANCEL_ERROR - ASSERT_L( (lua_gettop( L) == stk_base_) && ((lua_type( L, stk_base_) == LUA_TSTRING) || equal_unique_key( L, stk_base_, CANCEL_ERROR))); + ASSERT_L((lua_gettop(L) == stk_base_) && ((lua_type(L, stk_base_) == LUA_TSTRING) || CANCEL_ERROR.equals(L, stk_base_))); break; } } LUAG_FUNC( set_debug_threadname) { - DECLARE_CONST_UNIQUE_KEY( hidden_regkey, LG_set_debug_threadname); + // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator + constexpr UniqueKey hidden_regkey{ 0x79C0669AAAE04440ull }; // C s_lane structure is a light userdata upvalue Lane* s = (Lane*) lua_touserdata( L, lua_upvalueindex( 1)); luaL_checktype( L, -1, LUA_TSTRING); // "name" @@ -959,7 +960,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) { // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them - enum e_status st = (rc == 0) ? DONE : equal_unique_key( L, 1, CANCEL_ERROR) ? CANCELLED : ERROR_ST; + enum e_status st = (rc == 0) ? DONE : CANCEL_ERROR.equals(L, 1) ? CANCELLED : ERROR_ST; // Posix no PTHREAD_TIMEDJOIN: // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change @@ -1024,7 +1025,7 @@ LUAG_FUNC( register) } // crc64/we of string "GCCB_KEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( GCCB_KEY, 0xcfb1f046ef074e88); +static constexpr UniqueKey GCCB_KEY{ 0xcfb1f046ef074e88ull }; //--- // lane_ud = lane_new( function @@ -1267,7 +1268,7 @@ LUAG_FUNC( lane_new) // Store the gc_cb callback in the uservalue if( gc_cb_idx > 0) { - push_unique_key( L, GCCB_KEY); // func libs priority globals package required gc_cb lane uv k + GCCB_KEY.push(L); // func libs priority globals package required gc_cb lane uv k lua_pushvalue( L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb lua_rawset( L, -3); // func libs priority globals package required gc_cb lane uv } @@ -1307,7 +1308,7 @@ LUAG_FUNC( thread_gc) // if there a gc callback? lua_getiuservalue( L, 1, 1); // ud uservalue - push_unique_key( L, GCCB_KEY); // ud uservalue __gc + GCCB_KEY.push(L); // ud uservalue __gc lua_rawget( L, -2); // ud uservalue gc_cb|nil if( !lua_isnil( L, -1)) { @@ -1986,7 +1987,7 @@ LUAG_FUNC( configure) lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX lua_setfield( L, -2, "max_prio"); // settings M - push_unique_key( L, CANCEL_ERROR); // settings M CANCEL_ERROR + CANCEL_ERROR.push(L); // settings M CANCEL_ERROR lua_setfield( L, -2, "cancel_error"); // settings M STACK_MID( L, 2); // reference stack contains only the function argument 'settings' diff --git a/src/linda.cpp b/src/linda.cpp index fa27871..4cc356a 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -137,7 +137,7 @@ LUAG_FUNC( linda_send) ++ key_i; } - bool const as_nil_sentinel{ equal_unique_key(L, key_i, NIL_SENTINEL) };// if not nullptr, send() will silently send a single nil if nothing is provided + bool const as_nil_sentinel{ NIL_SENTINEL.equals(L, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided if( as_nil_sentinel) { // the real key to send data to is after the NIL_SENTINEL marker @@ -155,7 +155,7 @@ LUAG_FUNC( linda_send) if( as_nil_sentinel) { // send a single nil if nothing is provided - push_unique_key( L, NIL_SENTINEL); + NIL_SENTINEL.push(L); } else { @@ -243,7 +243,7 @@ LUAG_FUNC( linda_send) { case CANCEL_SOFT: // if user wants to soft-cancel, the call returns lanes.cancel_error - push_unique_key( L, CANCEL_ERROR); + CANCEL_ERROR.push(L); return 1; case CANCEL_HARD: @@ -397,7 +397,7 @@ LUAG_FUNC( linda_receive) { case CANCEL_SOFT: // if user wants to soft-cancel, the call returns CANCEL_ERROR - push_unique_key( L, CANCEL_ERROR); + CANCEL_ERROR.push(L); return 1; case CANCEL_HARD: @@ -458,7 +458,7 @@ LUAG_FUNC( linda_set) else // linda is cancelled { // do nothing and return lanes.cancel_error - push_unique_key( L, CANCEL_ERROR); + CANCEL_ERROR.push(L); pushed = 1; } } @@ -522,7 +522,7 @@ LUAG_FUNC( linda_get) else // linda is cancelled { // do nothing and return lanes.cancel_error - push_unique_key( L, CANCEL_ERROR); + CANCEL_ERROR.push(L); pushed = 1; } // an error can be raised if we attempt to read an unregistered function @@ -570,7 +570,7 @@ LUAG_FUNC( linda_limit) else // linda is cancelled { // do nothing and return lanes.cancel_error - push_unique_key( L, CANCEL_ERROR); + CANCEL_ERROR.push(L); pushed = 1; } } @@ -798,7 +798,7 @@ static void* linda_id( lua_State* L, DeepOp op_) } if( s) { - s->prelude.magic.value = DEEP_VERSION.value; + s->prelude.DeepPrelude::DeepPrelude(); SIGNAL_INIT( &s->read_happened); SIGNAL_INIT( &s->write_happened); s->U = universe_get( L); @@ -901,7 +901,7 @@ static void* linda_id( lua_State* L, DeepOp op_) lua_pushliteral( L, BATCH_SENTINEL); lua_setfield( L, -2, "batched"); - push_unique_key( L, NIL_SENTINEL); + NIL_SENTINEL.push(L); lua_setfield( L, -2, "null"); STACK_END( L, 1); diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h index 8a4ffb3..ae93e97 100644 --- a/src/macros_and_utils.h +++ b/src/macros_and_utils.h @@ -83,14 +83,14 @@ extern char const* debugspew_indent; // non-string keyed registry access #define REGISTRY_SET( L, key_, value_) \ { \ - push_unique_key( L, key_); \ + key_.push(L); \ value_; \ lua_rawset( L, LUA_REGISTRYINDEX); \ } #define REGISTRY_GET( L, key_) \ { \ - push_unique_key( L, key_); \ + key_.push(L); \ lua_rawget( L, LUA_REGISTRYINDEX); \ } diff --git a/src/tools.cpp b/src/tools.cpp index e6ebdba..ac2cf75 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -328,7 +328,7 @@ static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out) } // crc64/we of string "LOOKUPCACHE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( LOOKUPCACHE_REGKEY, 0x837a68dfc6fcb716); +static constexpr UniqueKey LOOKUPCACHE_REGKEY{ 0x837a68dfc6fcb716ull }; // inspired from tconcat() in ltablib.c static char const* luaG_pushFQN( lua_State* L, int t, int last, size_t* length) @@ -627,7 +627,7 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) /*---=== Inter-state copying ===---*/ // crc64/we of string "REG_MTID" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( REG_MTID, 0x2e68f9b4751584dc); +static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull }; /* * Get a unique ID for metatable at [i]. @@ -845,7 +845,7 @@ static bool lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mode static bool push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) { bool not_found_in_cache; // L2 - DECLARE_CONST_UNIQUE_KEY( p, lua_topointer( L, i)); + void const* p{ lua_topointer(L, i) }; ASSERT_L( L2_cache_i != 0); STACK_GROW( L2, 3); @@ -854,7 +854,7 @@ static bool push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, u // We don't need to use the from state ('L') in ID since the life span // is only for the duration of a copy (both states are locked). // push a light userdata uniquely representing the table - push_unique_key( L2, p); // ... p + lua_pushlightuserdata(L2, const_cast(p)); // ... p //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); @@ -864,7 +864,7 @@ static bool push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, u { lua_pop( L2, 1); // ... lua_newtable( L2); // ... {} - push_unique_key( L2, p); // ... {} p + lua_pushlightuserdata(L2, const_cast(p)); // ... {} p lua_pushvalue( L2, -2); // ... {} p {} lua_rawset( L2, L2_cache_i); // ... {} } @@ -1508,7 +1508,7 @@ static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache * The clone cache is a weak valued table listing all clones, indexed by their userdatapointer * fnv164 of string "CLONABLES_CACHE_KEY" generated at https://www.pelock.com/products/hash-calculator */ -static DECLARE_CONST_UNIQUE_KEY( CLONABLES_CACHE_KEY, 0xD04EE018B3DEE8F5); +static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; static bool copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t source_i_, LookupMode mode_, char const* upName_) { diff --git a/src/tools.h b/src/tools.h index 9214d85..928a149 100644 --- a/src/tools.h +++ b/src/tools.h @@ -47,7 +47,7 @@ void cleanup_allocator_function( Universe* U, lua_State* L); // ################################################################################################ // crc64/we of string "CONFIG_REGKEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( CONFIG_REGKEY, 0x31cd24894eae8624); // 'cancel_error' sentinel +static constexpr UniqueKey CONFIG_REGKEY{ 0x31cd24894eae8624ull }; // registry key to access the configuration // crc64/we of string "LOOKUP_REGKEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( LOOKUP_REGKEY, 0x5051ed67ee7b51a1); // 'cancel_error' sentinel +static constexpr UniqueKey LOOKUP_REGKEY{ 0x5051ed67ee7b51a1ull }; // registry key to access the lookup database diff --git a/src/uniquekey.h b/src/uniquekey.h index f219ab1..fb98628 100644 --- a/src/uniquekey.h +++ b/src/uniquekey.h @@ -2,21 +2,40 @@ #include "compat.h" -// Lua light userdata can hold a pointer. -struct s_UniqueKey +class UniqueKey { - void* value; -}; -typedef struct s_UniqueKey UniqueKey; + private: + + uintptr_t m_storage; + + public: + constexpr UniqueKey(uintptr_t val_) #if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations -#define MAKE_UNIQUE_KEY( p_) ((void*)((ptrdiff_t)(p_) & 0x7fffffffffffull)) + : m_storage{ val_ & 0x7fffffffffffull } #else // LUAJIT_FLAVOR() -#define MAKE_UNIQUE_KEY( p_) ((void*)(ptrdiff_t)(p_)) + : m_storage{ val_ } #endif // LUAJIT_FLAVOR() + { + } + constexpr UniqueKey(UniqueKey const& rhs_) = default; + constexpr bool operator!=(UniqueKey const& rhs_) const + { + return m_storage != rhs_.m_storage; + } + constexpr bool operator==(UniqueKey const& rhs_) const + { + return m_storage == rhs_.m_storage; + } -#define DECLARE_UNIQUE_KEY( name_) UniqueKey name_ -#define DECLARE_CONST_UNIQUE_KEY( name_, p_) UniqueKey const name_ = { MAKE_UNIQUE_KEY( p_)} - -#define push_unique_key( L, key_) lua_pushlightuserdata( L, key_.value) -#define equal_unique_key( L, i, key_) (lua_touserdata( L, i) == key_.value) + void push(lua_State* const L) const + { + // unfortunately, converting a scalar to a pointer must go through a C cast + lua_pushlightuserdata(L, (void*) m_storage); + } + bool equals(lua_State* const L, int i) const + { + // unfortunately, converting a scalar to a pointer must go through a C cast + return lua_touserdata(L, i) == (void*) m_storage; + } +}; diff --git a/src/universe.cpp b/src/universe.cpp index d5cc9e2..a31189c 100644 --- a/src/universe.cpp +++ b/src/universe.cpp @@ -37,7 +37,7 @@ THE SOFTWARE. #include "uniquekey.h" // crc64/we of string "UNIVERSE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ -static DECLARE_CONST_UNIQUE_KEY( UNIVERSE_REGKEY, 0x9f877b2cf078f17f); +static constexpr UniqueKey UNIVERSE_REGKEY{ 0x9f877b2cf078f17full }; // ################################################################################################ diff --git a/src/universe.h b/src/universe.h index 77b3ea5..0599302 100644 --- a/src/universe.h +++ b/src/universe.h @@ -12,8 +12,7 @@ extern "C" { #include "macros_and_utils.h" // forwards -struct s_DeepPrelude; -typedef struct s_DeepPrelude DeepPrelude; +struct DeepPrelude; struct s_Keepers; typedef struct s_Keepers Keepers; struct s_Lane; -- cgit v1.2.3-55-g6feb