github.com/aergoio/aergo@v1.3.1/contract/state_module.c (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  #include <string.h>
     7  #include <stdlib.h>
     8  #include <stdint.h>
     9  #include "vm.h"
    10  #include "system_module.h"
    11  
    12  #define STATE_MAP_ID            "__state_map__"
    13  #define STATE_ARRAY_ID          "__state_array__"
    14  #define STATE_VALUE_ID          "__state_value__"
    15  
    16  #define STATE_VAR_KEY_PREFIX    "_sv_"
    17  #define STATE_VAR_META_LEN      "_sv_meta-len_"
    18  #define STATE_VAR_META_TYPE     "_sv_meta-type_"
    19  
    20  #define STATE_MAX_DIMENSION 5
    21  
    22  static int state_map_delete(lua_State *L);
    23  static int state_array_append(lua_State *L);
    24  static int state_array_pairs(lua_State *L);
    25  
    26  /* map */
    27  
    28  typedef struct {
    29      char *id;
    30      int key_type;
    31      int dimension;
    32      char *key;
    33  } state_map_t;
    34  
    35  static int state_map(lua_State *L)
    36  {
    37      int argn = lua_gettop(L);
    38  
    39      state_map_t *m = lua_newuserdata(L, sizeof(state_map_t));   /* m */
    40      m->id = NULL;
    41      m->key_type = LUA_TNONE;
    42      m->key = NULL;
    43      if (luaL_isinteger(L, 1))
    44          m->dimension = luaL_checkint(L, 1);
    45      else if (argn == 0)
    46          m->dimension = 1;
    47      else
    48          luaL_typerror(L, 1, "integer");
    49  
    50      if (m->dimension > STATE_MAX_DIMENSION) {
    51          luaL_error(L, "dimension over max limit(%d): %d, state.map",
    52                     STATE_MAX_DIMENSION, m->dimension);
    53      }
    54      luaL_getmetatable(L, STATE_MAP_ID);                         /* m mt */
    55      lua_setmetatable(L, -2);                                    /* m */
    56      return 1;
    57  }
    58  
    59  static void state_map_check_index(lua_State *L, state_map_t *m)
    60  {
    61      /* m key */
    62      int key_type = lua_type(L, 2);
    63      int stored_type = m->key_type;
    64  
    65      if (key_type != LUA_TNUMBER && key_type != LUA_TSTRING) {
    66          luaL_error(L, "invalid key type: " LUA_QS ", state.map: " LUA_QS,
    67                     lua_typename(L, key_type), m->id);
    68      }
    69      if (stored_type == LUA_TNONE) {
    70          lua_pushcfunction(L, getItemWithPrefix);    /* m key f */
    71          lua_pushstring(L, m->id);                   /* m key f id */
    72          lua_pushstring(L, STATE_VAR_META_TYPE);     /* m key f id prefix */
    73          lua_call(L, 2, 1);                          /* m key t */
    74          if (!lua_isnil(L, -1)) {
    75              stored_type = luaL_checkint(L, -1);
    76              if (stored_type != LUA_TNUMBER && stored_type != LUA_TSTRING) {
    77                  luaL_error(L, "invalid stored key type: " LUA_QS ", state.map:",
    78                             lua_typename(L, stored_type), m->id);
    79              }
    80          }
    81  //  need hardfork
    82  //        m->key_type = stored_type;
    83          lua_pop(L, 1);
    84      }
    85      if (stored_type != LUA_TNONE && key_type != stored_type) {
    86          luaL_typerror(L, 2, lua_typename(L, stored_type));
    87      }
    88  }
    89  
    90  static void state_map_push_key(lua_State *L, state_map_t *m)
    91  {
    92      lua_pushstring(L, m->id);                       /* m key value f id */
    93      lua_pushstring(L, "-");
    94      lua_pushvalue(L, 2);                            /* m key value f id '-' key */
    95      lua_concat(L, 3);                               /* m key value f id-key */
    96  }
    97  
    98  static int state_map_get(lua_State *L)
    99  {
   100      int key_type = LUA_TNONE;
   101      int arg = lua_gettop(L);
   102      state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID); /* m key */
   103  
   104      key_type = lua_type(L, 2);
   105      if (key_type == LUA_TSTRING) {
   106          const char *method = lua_tostring(L, 2);
   107          if (method != NULL && strcmp(method, "delete") == 0) {
   108              lua_pushcfunction(L, state_map_delete);
   109              return 1;
   110          }
   111      }
   112  
   113      state_map_check_index(L, m);
   114  
   115      if (m->dimension > 1) {
   116          state_map_t *subm = lua_newuserdata(L, sizeof(state_map_t));   /* m */
   117          subm->id = strdup(m->id);
   118          subm->key_type = m->key_type;
   119          subm->dimension = m->dimension - 1;
   120  
   121          luaL_getmetatable(L, STATE_MAP_ID);                         /* m mt */
   122          lua_setmetatable(L, -2);                                    /* m */
   123          if (m->key == NULL) {
   124              subm->key = strdup(lua_tostring(L, 2));
   125          }
   126          else {
   127              lua_pushstring(L, m->key);     /* a key value f id '-' */
   128              lua_pushstring(L, "-");     /* a key value f id '-' */
   129              lua_pushvalue(L, 2);        /* m key f id-key prefix */
   130              lua_concat(L, 3);           /* m key value f id-key */
   131              subm->key = strdup(lua_tostring(L, -1));
   132              lua_pop(L, 1);
   133          }
   134          return 1;
   135      }
   136  
   137      lua_pushcfunction(L, getItemWithPrefix);        /* m key f */
   138      if (m->key != NULL) {
   139          lua_pushstring(L, m->key);
   140          lua_pushstring(L, "-");
   141          lua_pushvalue(L, 2);
   142          lua_concat(L, 3);
   143          lua_replace(L, 2);
   144      }
   145      state_map_push_key(L, m);                       /* m key f id-key */
   146      if (arg == 3) {
   147          lua_pushvalue(L, 3);
   148      }
   149  
   150      lua_pushstring(L, STATE_VAR_KEY_PREFIX);        /* m key value f id-key value prefix */
   151      lua_call(L, arg, 1);                            /* m key rv */
   152      return 1;
   153  }
   154  
   155  static int state_map_set(lua_State *L)
   156  {
   157      /* m key value */
   158      int key_type = LUA_TNONE;
   159      state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID);
   160  
   161      key_type = lua_type(L, 2);
   162  
   163      if (m->dimension > 1) {
   164         luaL_error(L, "not permitted to set intermediate dimension of map");
   165      }
   166      if (key_type == LUA_TSTRING) {
   167          const char *method = lua_tostring(L, 2);
   168          if (method != NULL && strcmp(method, "delete") == 0) {
   169              luaL_error(L, "can't use " LUA_QL("delete") " as a key");
   170          }
   171      }
   172      state_map_check_index(L, m);
   173  
   174      if (m->key_type == LUA_TNONE) {
   175          lua_pushcfunction(L, setItemWithPrefix);    /* m key f */
   176          lua_pushstring(L, m->id);                   /* m key f id */
   177          lua_pushinteger(L, key_type);               /* m key f id type */
   178          lua_pushstring(L, STATE_VAR_META_TYPE);     /* m key f id type prefix */
   179          lua_call(L, 3, 0);                          /* m key */
   180          m->key_type = key_type;
   181      }
   182      luaL_checkany(L, 3);
   183      lua_pushcfunction(L, setItemWithPrefix);        /* m key value f */
   184  
   185      if (m->key != NULL) {
   186          lua_pushstring(L, m->key);
   187          lua_pushstring(L, "-");
   188          lua_pushvalue(L, 2);
   189          lua_concat(L, 3);
   190          lua_replace(L, 2);
   191      }
   192  
   193      state_map_push_key(L, m);                       /* m key value f id-key */
   194      lua_pushvalue(L, 3);                            /* m key value f id-key value */
   195      lua_pushstring(L, STATE_VAR_KEY_PREFIX);        /* m key value f id-key value prefix */
   196      lua_call(L, 3, 0);                              /* t key value */
   197      return 0;
   198  }
   199  
   200  static int state_map_delete(lua_State *L)
   201  {
   202      /* m key */
   203      state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID);
   204  
   205      if (m->dimension > 1) {
   206         luaL_error(L, "not permitted to set intermediate dimension of map");
   207      }
   208      state_map_check_index(L, m);
   209      lua_pushcfunction(L, delItemWithPrefix);        /* m key f */
   210  
   211      if (m->key != NULL) {
   212          lua_pushstring(L, m->key);
   213          lua_pushstring(L, "-");
   214          lua_pushvalue(L, 2);
   215          lua_concat(L, 3);
   216          lua_replace(L, 2);
   217      }
   218      state_map_push_key(L, m);                       /* m key f id-key */
   219      lua_pushstring(L, STATE_VAR_KEY_PREFIX);        /* m key f id-key prefix */
   220      lua_call(L, 2, 1);                              /* m key rv */
   221      return 0;
   222  }
   223  
   224  static int state_map_gc(lua_State *L)
   225  {
   226      state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID);
   227      if (m->id) {
   228          free(m->id);
   229          m->id = NULL;
   230      }
   231      if (m->key) {
   232          free(m->key);
   233          m->key = NULL;
   234      }
   235      return 0;
   236  }
   237  
   238  /* array */
   239  
   240  typedef struct {
   241      char *id;
   242      int is_fixed;
   243      int dimension;
   244      int32_t *lens;
   245      char *key;
   246  } state_array_t;
   247  
   248  static int state_array(lua_State *L)
   249  {
   250      int is_fixed;
   251      state_array_t *arr;
   252      int dimension = lua_gettop(L);
   253      int32_t *lens = NULL;
   254  
   255      is_fixed = dimension != 0;
   256  
   257      if (dimension > STATE_MAX_DIMENSION) {
   258          luaL_error(L, "dimension over max limit(%d): %d, state.array",
   259                     STATE_MAX_DIMENSION, dimension);
   260      }
   261      if (is_fixed) {
   262          int i;
   263          lens = malloc(sizeof(int32_t) * dimension);
   264          for (i = 1; i <= dimension; i++) {
   265              if (!luaL_isinteger(L, i)) {
   266                  luaL_typerror(L, i, "integer");
   267              }
   268              lens[i -1] = luaL_checkint(L, i);                      /* size */
   269              luaL_argcheck(L, (lens[i - 1] > 0), i, "the array length must be greater than zero");
   270          }
   271      }
   272      arr = lua_newuserdata(L, sizeof(state_array_t));    /* size a */
   273      luaL_getmetatable(L, STATE_ARRAY_ID);               /* size a mt */
   274      lua_setmetatable(L, -2);                            /* size a */
   275      arr->lens = lens;
   276      arr->id = NULL;
   277      arr->dimension = dimension;
   278      arr->is_fixed = is_fixed;
   279      arr->key = NULL;
   280      return 1;
   281  }
   282  
   283  static int state_array_len(lua_State *L)
   284  {
   285      state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID);
   286      if (arr->lens == NULL)
   287          lua_pushinteger(L, 0);
   288      else
   289          lua_pushinteger(L, arr->lens[0]);
   290      return 1;
   291  }
   292  
   293  static void state_array_load_len(lua_State *L, state_array_t *arr)
   294  {
   295      if (!arr->is_fixed && arr->lens == NULL) {
   296          lua_pushcfunction(L, getItemWithPrefix);    /* a i f */
   297          lua_pushstring(L, arr->id);                 /* a i f id */
   298          lua_pushstring(L, STATE_VAR_META_LEN);      /* a i f id prefix */
   299          lua_call(L, 2, 1);                          /* a i n */
   300          arr->lens = malloc(sizeof(int32_t));
   301          arr->lens[0] = luaL_optinteger(L, -1, 0);
   302          lua_pop(L, 1);
   303      }
   304  }
   305  
   306  static void state_array_checkarg(lua_State *L, state_array_t *arr)
   307  {
   308      int idx;
   309      if (!luaL_isinteger(L, 2)) {
   310          luaL_typerror(L, 2, "integer");
   311      }
   312      idx = luaL_checkint(L, 2);
   313      luaL_argcheck(L, idx >= 1 && idx <= arr->lens[0], 2, "index out of range");
   314  }
   315  
   316  static void state_array_push_key(lua_State *L, const char *id)
   317  {
   318      lua_pushstring(L, id);      /* a key value f id */
   319      lua_pushstring(L, "-");     /* a key value f id '-' */
   320      lua_pushvalue(L, 2);        /* m key value f id '-' key */
   321      lua_concat(L, 3);           /* m key value f id-key */
   322  }
   323  
   324  static int state_array_get(lua_State *L)
   325  {
   326      state_array_t *arr;
   327      int arg = lua_gettop(L);
   328      int key_type = LUA_TNONE;
   329  
   330      arr = luaL_checkudata(L, 1, STATE_ARRAY_ID);
   331      state_array_load_len(L, arr);
   332  
   333      if (lua_type(L, 2) == LUA_TSTRING) {            /* methods */
   334          const char *method = lua_tostring(L, 2);
   335          if (strcmp(method, "append") == 0) {
   336              lua_pushcfunction(L, state_array_append);
   337              return 1;
   338          } else if (strcmp(method, "ipairs") == 0) {
   339              lua_pushcfunction(L, state_array_pairs);
   340              return 1;
   341          } else if (strcmp(method, "length") == 0) {
   342              lua_pushcfunction(L, state_array_len);
   343              return 1;
   344          }
   345          luaL_typerror(L, 2, "integer");
   346      }
   347      if (arr->dimension > 1) {
   348          state_array_t *suba = lua_newuserdata(L, sizeof(state_array_t));   /* m */
   349          suba->id = strdup(arr->id);
   350          suba->dimension = arr->dimension - 1;
   351          suba->lens = malloc(sizeof(int32_t) * suba->dimension);
   352          suba->is_fixed = arr->is_fixed;
   353          memcpy(suba->lens, arr->lens + 1, sizeof(int32_t) * suba->dimension);
   354  
   355          luaL_getmetatable(L, STATE_ARRAY_ID);                         /* m mt */
   356          lua_setmetatable(L, -2);                                    /* m */
   357          if (arr->key == NULL) {
   358              suba->key = strdup(lua_tostring(L, 2));
   359          }
   360          else {
   361              lua_pushstring(L, arr->key);     /* a key value f id '-' */
   362              lua_pushstring(L, "-");     /* a key value f id '-' */
   363              lua_pushvalue(L, 2);        /* m key f id-key prefix */
   364              lua_concat(L, 3);           /* m key value f id-key */
   365              suba->key = strdup(lua_tostring(L, -1));
   366              lua_pop(L, 1);
   367          }
   368          return 1;
   369      }
   370      if (arg == 3) {
   371          lua_pushvalue(L, 2);
   372      }
   373      state_array_checkarg(L, arr);                   /* a i */
   374      lua_pushcfunction(L, getItemWithPrefix);        /* a i f */
   375  
   376      if (arr->key != NULL) {
   377          lua_pushstring(L, arr->key);
   378          lua_pushstring(L, "-");
   379          lua_pushvalue(L, 2);
   380          lua_concat(L, 3);
   381          lua_replace(L, 2);
   382      }
   383      state_array_push_key(L, arr->id);               /* a i f id-i */
   384      if (arg == 3) {
   385          lua_pushvalue(L, 3);                        /* a i s i f id-i s */
   386      }
   387      lua_pushstring(L, STATE_VAR_KEY_PREFIX);        /* a i f id-i prefix */
   388      lua_call(L, arg, 1);                              /* a i rv */
   389      return 1;
   390  }
   391  
   392  static int state_array_set(lua_State *L)
   393  {
   394      state_array_t *arr;
   395  
   396      arr = luaL_checkudata(L, 1, STATE_ARRAY_ID);
   397  
   398      if (arr->dimension > 1) {
   399         luaL_error(L, "not permitted to set intermediate dimension of array");
   400      }
   401      state_array_load_len(L, arr);
   402  
   403      state_array_checkarg(L, arr);                   /* a i v */
   404      if (arr->key != NULL) {
   405          lua_pushstring(L, arr->key);
   406          lua_pushstring(L, "-");
   407          lua_pushvalue(L, 2);
   408          lua_concat(L, 3);
   409          lua_replace(L, 2);
   410      }
   411      lua_pushcfunction(L, setItemWithPrefix);        /* a i v f */
   412      state_array_push_key(L, arr->id);               /* a i v f id-i */
   413      lua_pushvalue(L, 3);                            /* a i v f id-i v */
   414      lua_pushstring(L, STATE_VAR_KEY_PREFIX);        /* a i v f id-i v prefix */
   415      lua_call(L, 3, 0);                              /* a i v */
   416      return 0;
   417  }
   418  
   419  static int state_array_append(lua_State *L)
   420  {
   421      state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID);
   422      luaL_checkany(L, 2);
   423      if (arr->is_fixed) {
   424          luaL_error(L, "the fixed array cannot use " LUA_QL("append") " method");
   425      }
   426      if (arr->lens[0] + 1 <= 0) {
   427          luaL_error(L, "state.array " LUA_QS " overflow", arr->id);
   428      }
   429      arr->lens[0]++;
   430      lua_pushcfunction(L, state_array_set);          /* a v f */
   431      lua_pushvalue(L, 1);                            /* a v f a */
   432      lua_pushinteger(L, arr->lens[0]);                   /* a v f a i */
   433      lua_pushvalue(L, 2);                            /* a v f a i v */
   434      lua_call(L, 3, 0);
   435      lua_pushcfunction(L, setItemWithPrefix);
   436      lua_pushstring(L, arr->id);
   437      lua_pushinteger(L, arr->lens[0]);
   438      lua_pushstring(L, STATE_VAR_META_LEN);
   439      lua_call(L, 3, 0);
   440      return 0;
   441  }
   442  
   443  static int state_array_gc(lua_State *L)
   444  {
   445      state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID);
   446      if (arr->id) {
   447          free(arr->id);
   448          arr->id = NULL;
   449      }
   450      if (arr->lens) {
   451          free(arr->lens);
   452          arr->lens = NULL;
   453      }
   454      if (arr->key) {
   455          free(arr->key);
   456          arr->key = NULL;
   457      }
   458      return 0;
   459  }
   460  
   461  static int state_array_iter(lua_State *L)
   462  {
   463      state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID);
   464      int i = luaL_checkint(L, 2);
   465      i = i + 1;
   466      if (i <= arr->lens[0]) {
   467          lua_pushinteger(L, i);
   468          lua_pushcfunction(L, state_array_get);
   469          lua_pushvalue(L, 1);
   470          lua_pushinteger(L, i);
   471          lua_call(L, 2, 1);
   472          return 2;
   473      }
   474      return 0;
   475  }
   476  
   477  static int state_array_pairs(lua_State *L)
   478  {
   479      luaL_checkudata(L, 1, STATE_ARRAY_ID);
   480      lua_pushcfunction(L, state_array_iter);
   481      lua_pushvalue(L, 1);
   482      lua_pushinteger(L, 0);
   483      return 3;
   484  }
   485  
   486  /* scalar value */
   487  
   488  typedef struct {
   489      char *id;
   490  } state_value_t;
   491  
   492  static int state_value(lua_State *L)
   493  {
   494      state_value_t *v = lua_newuserdata(L, sizeof(state_value_t));   /* v */
   495      v->id = NULL;
   496      luaL_getmetatable(L, STATE_VALUE_ID);                           /* v mt */
   497      lua_setmetatable(L, -2);                                        /* v */
   498      return 1;
   499  }
   500  
   501  static int state_value_get(lua_State *L)
   502  {
   503      state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID);
   504      lua_pushcfunction(L, getItemWithPrefix);        /* v f */
   505      lua_pushstring(L, v->id);                       /* v f id */
   506      lua_pushstring(L, STATE_VAR_KEY_PREFIX);        /* v f id prefix */
   507      lua_call(L, 2, 1);                              /* v rv */
   508      return 1;
   509  }
   510  
   511  static int state_value_snapget(lua_State *L)
   512  {
   513      int arg = lua_gettop(L);
   514      state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID);   /* v */
   515      lua_pushcfunction(L, getItemWithPrefix);                    /* v f */
   516      lua_pushstring(L, v->id);                                   /* v f id */
   517      if (arg == 2) {
   518          lua_pushvalue(L, 2);
   519      }
   520      lua_pushstring(L, STATE_VAR_KEY_PREFIX);                    /* v f id prefix */
   521      lua_call(L, arg + 1, 1);                                    /* v rv */
   522      return 1;
   523  }
   524  
   525  static int state_value_set(lua_State *L)
   526  {
   527      state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID);   /* v */
   528      luaL_checkany(L, 2);
   529      lua_pushcfunction(L, setItemWithPrefix);        /* t f */
   530      if (v->id == NULL) {
   531          luaL_error(L, "invalid state.value: (nil)");
   532      }
   533      lua_pushstring(L, v->id);                       /* v f id */
   534      lua_pushvalue(L, 2);                            /* t f id value */
   535      lua_pushstring(L, STATE_VAR_KEY_PREFIX);        /* v f id value prefix */
   536      lua_call(L, 3, 0);                              /* v */
   537      return 0;
   538  }
   539  
   540  static int state_value_gc(lua_State *L)
   541  {
   542      state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID);
   543      if (v->id) {
   544          free(v->id);
   545          v->id = NULL;
   546      }
   547      return 0;
   548  }
   549  
   550  /* global variable */
   551  
   552  static void insert_var(lua_State *L, const char *var_name)
   553  {
   554      lua_getglobal(L, "abi");                        /* "type_name" m */
   555      lua_getfield(L, -1, "register_var");            /* "type_name" m f */
   556      lua_pushstring(L, var_name);                    /* "type_name" m f var_name */
   557      lua_pushvalue(L, -4);                           /* "type_name" m f var_name "type_name" */
   558      lua_call(L, 2, 0);                              /* "type_name" m */
   559      lua_pop(L, 2);
   560  }
   561  
   562  static int state_var(lua_State *L)
   563  {
   564      const char *var_name;
   565      state_map_t *m = NULL;
   566      state_array_t *arr = NULL;
   567      state_value_t *v = NULL;
   568  
   569      luaL_checktype(L, 1, LUA_TTABLE);                   /* T */
   570      lua_pushnil(L);                                     /* T nil ; push the first key */
   571      while (lua_next(L, -2) != 0) {                      /* T key value */
   572          var_name = luaL_checkstring(L, -2);
   573          if (!lua_isuserdata(L, -1)) {
   574              lua_pushfstring(L, "bad argument " LUA_QS ": state.value, state.map or state.array expected, got %s",
   575                              var_name, lua_typename(L, lua_type(L, -1)));
   576              lua_error(L);
   577          }
   578  
   579          m = luaL_testudata(L, -1, STATE_MAP_ID);
   580          if (m != NULL) {
   581              m->id = strdup(var_name);
   582              goto found;
   583          }
   584  
   585          arr = luaL_testudata(L, -1, STATE_ARRAY_ID);
   586          if (arr != NULL) {
   587              arr->id = strdup(var_name);                 /* T key value */
   588              goto found;
   589          }
   590  
   591          v = luaL_testudata(L, -1, STATE_VALUE_ID);
   592          if (v != NULL) {
   593              v->id = strdup(var_name);
   594          } else {
   595              lua_pushfstring(L, "bad argument " LUA_QS ": state.value, state.map or state.array expected", var_name);
   596              lua_error(L);
   597          }
   598  
   599  found:
   600          lua_newtable(L);
   601          insert_var(L, var_name);
   602          lua_setglobal(L, var_name);                 /* T key */
   603      }
   604      return 0;
   605  }
   606  
   607  static int state_get_snap(lua_State *L)
   608  {
   609      state_map_t *m = NULL;
   610      state_array_t *arr = NULL;
   611      state_value_t *v = NULL;
   612  
   613      if (!lua_isuserdata(L, 1)) {
   614          luaL_typerror(L, 1, "state.value, state.map or state.array");
   615      }
   616  
   617      m = luaL_testudata(L, 1, STATE_MAP_ID);
   618      if (m != NULL) {
   619          if (lua_gettop(L) != 3)
   620              luaL_error(L, "invalid argument at getsnap, need (state.map, key, blockheight)");
   621          return state_map_get(L);
   622      }
   623  
   624      arr = luaL_testudata(L, 1, STATE_ARRAY_ID);
   625      if (arr != NULL) {
   626          if (lua_gettop(L) != 3)
   627              luaL_error(L, "invalid argument at getsnap, need (state.array, index, blockheight)");
   628          return state_array_get(L);
   629      }
   630  
   631      v = luaL_testudata(L, 1, STATE_VALUE_ID);
   632      if (v != NULL) {
   633          if (lua_gettop(L) != 2)
   634              luaL_error(L, "invalid argument at getsnap, need (state.value, blockheight)");
   635          return state_value_snapget(L);
   636      }
   637  
   638      luaL_typerror(L, 1, "state.value, state.map or state.array");
   639      return 0;
   640  }
   641  
   642  int luaopen_state(lua_State *L)
   643  {
   644      static const luaL_Reg state_map_metas[] = {
   645          {"__index",  state_map_get},
   646          {"__newindex", state_map_set},
   647          {"__gc", state_map_gc},
   648          {NULL, NULL}
   649      };
   650  
   651      static const luaL_Reg state_array_metas[] = {
   652          {"__index",  state_array_get},
   653          {"__newindex", state_array_set},
   654          {"__len", state_array_len},
   655          {"__gc", state_array_gc},
   656          {NULL, NULL}
   657      };
   658  
   659      static const luaL_Reg state_value_methods[] = {
   660          {"get", state_value_get},
   661          {"set", state_value_set},
   662          {NULL, NULL}
   663      };
   664  
   665      static const luaL_Reg state_lib[] = {
   666          {"map", state_map},
   667          {"array", state_array},
   668          {"value", state_value},
   669          {"var", state_var},
   670          {"getsnap", state_get_snap},
   671          {NULL, NULL}
   672      };
   673  
   674      luaL_newmetatable(L, STATE_MAP_ID);
   675      luaL_register(L, NULL, state_map_metas);
   676  
   677      luaL_newmetatable(L, STATE_ARRAY_ID);
   678      luaL_register(L, NULL, state_array_metas);
   679  
   680      luaL_newmetatable(L, STATE_VALUE_ID);
   681      lua_pushvalue(L, -1);
   682      lua_setfield(L, -2, "__index");
   683      luaL_register(L, NULL, state_value_methods);
   684      lua_pushcfunction(L, state_value_gc);
   685      lua_setfield(L, -2, "__gc");
   686  
   687      luaL_register(L, "state", state_lib);
   688  
   689      return 1;
   690  }