Commits

Roma Sokolov committed 794d961

draft of kv store on top of berkley db

Comments (0)

Files changed (11)

kvastore/Makefile

+PROJECT := kvastore
+
+COMPILER := gcc
+
+SOURCES := kvas_cmd.c $(PROJECT).h $(PROJECT).c
+ALL_H :=$(filter %.h,$(SOURCES))
+ALL_C :=$(filter %.c,$(SOURCES))
+
+ALL_O:=$(foreach s,$(ALL_C), $(basename $(notdir $(s))).o)
+
+FLAGS :=-Wall -Wextra -Werror -pedantic-errors
+LINKPKG := -pthread -lc -ldb-4.8 -lev -ltcmalloc
+DIRECTORIES :=-L/usr/local/lib -L/usr/local/lib/google -I/usr/local/include
+
+$(PROJECT):
+	gcc $(ALL_H) $(DIRECTORIES) $(FLAGS) -c
+	gcc $(ALL_C) $(DIRECTORIES) $(FLAGS) -c
+	gcc $(ALL_O) $(DIRECTORIES) $(FLAGS) $(LINKPKG) -o $(PROJECT).out
+
+clean:
+	rm -rf $(ALL_O)
+	rm -rf $(PROJECT).out
+
+install:
+	echo "no way!";

kvastore/TODO.txt

+kvas_ctl app -- start, restart, etc.
+ipc socket for comunicating with main app
+argv for command
+kvas_ctl -- several threads with PULL from main app
+ipc socket for communicate with _ctl.
+
+
+
+
+____________________
+ev_read , ev_write data not pointers.
+set_allocator, where fault
+  method = find_method(method_key);
+  switch(method) {
+  case KVAS_CMD_GET:
+
+    break;
+  case KVAS_CMD_PUT:
+    break;
+  default:
+    fprintf(stderr, "Unknown command: %s!\n", method_key);
+    return -1;
+  }

kvastore/kvas_cmd.c

+#include <stdio.h>
+#include <string.h>
+
+#include "tcmalloc.h"
+
+int find_key_in_array(const char* key, const char* array[], int size) {
+  int i;
+  for(i=0; i < size; i++) {
+    if (!strcmp(key, array[i])) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+int split_cmd(const char* source, char*** res) {
+  char* running;
+  char* token;
+  const char delim[] = " ";
+
+  running = (char* ) tc_malloc(strlen(source) + 1);
+  running = strcpy(running, source);
+  token = strsep(&running, delim);
+  if(token != NULL && running != NULL){
+    (*res)[0] = (char* ) tc_malloc(strlen(token) + 1);
+    (*res)[0] = strcpy((*res)[0], token);
+    (*res)[1] = (char* ) tc_malloc(strlen(running) + 1);
+    (*res)[1] = strcpy((*res)[1], running);
+
+    tc_free(running);
+    return 0;
+  }
+  return -1;
+}

kvastore/kvas_cmd.h

+#ifndef KVAS_CMD_INCLUDED
+#define KVAS_CMD_INCLUDED
+
+#define KVAS_CMD_GET 0
+#define KVAS_CMD_PUT 1
+
+#define KVAS_METHODS_COUNT 2
+
+#define find_method(method) find_key_in_array(method, METHODS, KVAS_METHODS_COUNT)
+
+const char* METHODS[] = {"get", "put"};
+
+int find_key_in_array(const char*, const char* [], int);
+int split_cmd(const char*, char***);
+#endif

kvastore/kvas_data.h

+#ifndef KVAS_DATA_INCLUDED
+#define KVAS_DATA_INCLUDED
+
+typedef struct client_t {
+  int fd;
+  ev_io* ev_write;
+  ev_io* ev_read;
+  int method;
+  char* data; /* method specific data */
+  char* error;
+} client_t;
+
+#endif

kvastore/kvastore.c

+#include "kvastore.h"
+
+#define PR(x, y) printf("%s %x\n", x, (unsigned)y)
+
+static void write_cb(struct ev_loop *loop, struct ev_io *w, int revents)
+{
+
+  client_t *client;
+
+  printf("started\n");
+  client = ((client_t*) (((char*)w) - offsetof(client_t, ev_read)));
+  if (revents & EV_WRITE){
+    if(client->error != NULL) {
+      write(client->fd, client->error, strlen(client->error));
+    } else {
+      write(client->fd, client->data, strlen(client->data));
+    }
+    ev_io_stop(EV_A_ w);
+  }
+  close(client->fd);
+
+  printf("end write_cb\n");
+}
+
+static void read_cb(struct ev_loop *loop, struct ev_io *w, int revents)
+{
+	char rbuff[1024];
+    char** cmd_data;
+    int method;
+    int ret_code;
+    client_t *client;
+
+    client = ((client_t*) (((char*)w) - offsetof(client_t, ev_read)));
+
+    PR("in read w = ", w);
+    PR("Client created", client);
+
+    if (revents & EV_READ){
+      UNUSED(read(w->fd, &rbuff, 1024));
+
+      cmd_data = (char**) tc_malloc(sizeof(char*) * 2);
+      PR("cmd data allocated", cmd_data);
+      ret_code = split_cmd(rbuff, &cmd_data);
+
+      CHECKSERRVCB(ret_code < 0, "Malformed message. Should be `cmd args`",
+                 client, tc_free(cmd_data));
+
+      method = find_method(cmd_data[0]);
+      CHECKSERRVCB(method < 0, "Unknown method",
+                   client,
+                   (tc_free(cmd_data[0]), tc_free(cmd_data[1]),
+                    tc_free(cmd_data)));
+
+      set_method(client, method, cmd_data[1]);
+      PR("Method set", client->data);
+      tc_free(cmd_data[0]);
+      tc_free(cmd_data[1]);
+      tc_free(cmd_data);
+      printf("data freed \n");
+	}
+
+    PR("Before allocation", client->ev_write);
+    S_ALLOC(client->ev_write, sizeof(ev_io), ev_io*);
+    PR("after allocation", client->ev_write);
+	ev_io_stop(EV_A_ w);
+	ev_io_init(client->ev_write, write_cb, client->fd, EV_WRITE);
+    PR("after init", client->ev_write);
+	ev_io_start(loop, client->ev_write);
+    printf("start\n");
+    printf("end read_cb\n");
+}
+
+static void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents)
+{
+	int client_fd;
+    client_t* client;
+    int ret_code;
+    struct sockaddr_in client_addr;
+    socklen_t client_len = sizeof(client_addr);
+
+    PR("in acceptd cb w = ", w);
+
+    client_fd = accept(w->fd, (struct sockaddr *)&client_addr, &client_len);
+    if (client_fd == -1) {
+      return;
+    }
+    client = (client_t*)tc_malloc(sizeof(client_t));
+    PR("Client allocated", client);
+    init_client(client);
+
+    client->fd = client_fd;
+    PR("read before", client->ev_read);
+    S_ALLOC(client->ev_read, sizeof(ev_io), ev_io*);
+    PR("read after", client->ev_read);
+
+	ret_code = setnonblock(client_fd);
+    if (ret_code < 0) {
+      return;
+    }
+	ev_io_init(client->ev_read, read_cb, client->fd, EV_READ);
+    PR("read after init", client->ev_read);
+	ev_io_start(loop, client->ev_read);
+    UNUSED(revents);
+    printf("end accept_Cb\n");
+}
+
+
+
+int main(/* int argc, char* argv[] */){
+  DB_ENV* kvas_env;
+  DB* kvas_db;
+  int ret_code, listen_fd, reuseaddr_on;
+  ev_io* ev_accept;
+  struct ev_loop* loop = EV_DEFAULT;
+  struct sockaddr_in listen_addr;
+
+  /* ev_set_allocator((realloc_t)tc_realloc); */
+  ev_accept = tc_malloc(sizeof(ev_io));
+  PR("Accept allocated", ev_accept);
+
+  listen_fd = socket(AF_INET, SOCK_STREAM, 0);
+  CHECKPERR(listen_fd < 0, "Cannot create socket");
+  ret_code = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,
+                        &reuseaddr_on, sizeof(reuseaddr_on));
+  CHECKPERR(reuseaddr_on == -1, "Cannot set socket option");
+  memset(&listen_addr, 0, sizeof(listen_addr));
+  listen_addr.sin_family = AF_INET;
+  listen_addr.sin_addr.s_addr = INADDR_ANY;
+  listen_addr.sin_port = htons(6666);
+
+  ret_code = bind(listen_fd, (struct sockaddr*)&listen_addr,
+                  sizeof(listen_addr));
+  CHECKPERR(ret_code < 0, "Cannot bind socket");
+  ret_code = listen(listen_fd, 10);
+  CHECKPERR(ret_code < 0, "Cannot listen");
+  ret_code = setnonblock(listen_fd);
+  CHECKPERR(ret_code < 0, "Cannot set nonblocking mode");
+
+  ret_code = init_db(&kvas_env, &kvas_db, ".", "kvas_db.db");
+  if(ret_code)
+    return -1;
+
+  ev_io_init(ev_accept, accept_cb, listen_fd, EV_READ);
+  ev_io_start(loop, ev_accept);
+  ev_loop(loop, 0);
+
+  close_db(kvas_env, kvas_db);
+  return 0;
+}
+
+int init_db(DB_ENV** env, DB** db, const char* path, const char* name) {
+  u_int32_t db_flags;
+  u_int32_t env_flags;
+  int ret_code;
+
+  env_flags = DB_CREATE | DB_INIT_MPOOL;
+  db_flags = DB_CREATE;
+
+  ret_code = db_env_create(env, 0);
+  CHECKDBERR(ret_code, "Cannot create enviroment handle");
+
+  ret_code = (*env)->open(*env, path, env_flags, 0);
+  CHECKDBERR(ret_code, "Cannot open environment");
+
+  ret_code = db_create(db, *env, 0);
+  CHECKDBERR(ret_code, "Cannot create database");
+
+  ret_code = (*db)->open(*db, NULL, name,
+                         NULL, DB_HASH, db_flags, 0);
+  CHECKDBERR(ret_code, "Cannot open database");
+
+  return 0;
+}
+
+void close_db(DB_ENV* env, DB* db) {
+  if (db != NULL) {
+    db->close(db, 0);
+  }
+
+  if (env != NULL) {
+    env->close(env, 0);
+  }
+}
+
+
+void set_error(client_t* client, const char* error_msg) {
+  S_ALLOC(client->error, strlen(error_msg) + 1, char*);
+  client->error = strcpy(client->error, error_msg);
+}
+
+void set_method(client_t* client, int method, const char* data) {
+  client->method = method;
+  S_ALLOC(client->data, strlen(data) + 1, char*);
+  client->data = strcpy(client->data, data);
+}
+
+int setnonblock(int fd) {
+    int flags;
+
+    flags = fcntl(fd, F_GETFL);
+    if (flags < 0)
+            return flags;
+    flags |= O_NONBLOCK;
+    if (fcntl(fd, F_SETFL, flags) < 0)
+            return -1;
+
+    return 0;
+}
+
+void init_client(client_t* client) {
+  client->error = NULL;
+  client->ev_read = NULL;
+  client->ev_write = NULL;
+  client->data = NULL;
+  client-> method = -1;
+}

kvastore/kvastore.h

+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stddef.h>
+
+#include <pthread.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <db48/db.h>
+
+#include <ev.h>
+
+#include <zmq.h>
+
+#include "tcmalloc.h"
+
+#include "kvas_cmd.h"
+#include "kvas_data.h"
+
+#define CHECKDBERR(code, msg) if(code){\
+  fprintf(stderr, "%s: %s\n", msg, db_strerror(code));\
+  return -1;}
+
+#define S_ALLOC(ptr, size, type) if(ptr == NULL) { \
+    ptr = (type) tc_malloc(size);                  \
+  } else {                                         \
+    ptr = (type) tc_realloc(ptr, size); }
+
+#define PERR_(msg, ret) fprintf(stderr, "%s\n", msg); return ret
+#define PERR(msg) PERR_(msg, -1)
+#define PERRV(msg) PERR_(msg, )
+#define SERR_(msg, obj, ret) set_error(obj, msg); return ret
+#define SERR(msg, obj) SERR_(msg, obj, -1)
+#define SERRV(msg, obj) SERR_(msg, obj, )
+#define COND(cond, body) if(cond){body;}
+#define CHECKPERR(cond, msg) COND(cond, PERR(msg))
+#define CHECKPERRV(cond, msg) COND(cond, PERRV(msg))
+#define CHECKSERRV(cond, msg, obj) COND(cond, SERRV(msg, obj))
+#define CHECKSERRVCB(cond, msg, obj, cb) COND(cond, cb; SERRV(msg, obj))
+
+#define UNUSED(x) ((void)(x))
+
+typedef void *(*realloc_t)(void *ptr, long size);
+
+int init_db(DB_ENV**, DB**, const char*, const char*);
+void close_db(DB_ENV*, DB*);
+
+int setnonblock(int);
+
+void set_error(client_t*, const char*);
+void set_method(client_t*, int, const char*);
+
+void init_client(client_t*);

kvastore/tcmalloc.h

+/* Copyright (c) 2003, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ---
+ * Author: Sanjay Ghemawat <opensource@google.com>
+ *         .h file by Craig Silverstein <opensource@google.com>
+ */
+
+#ifndef TCMALLOC_TCMALLOC_H_
+#define TCMALLOC_TCMALLOC_H_
+
+/*  __THROW is defined in glibc systems.  It means, counter-intuitively, */
+/*  "This function will never throw an exception."  It's an optional */
+/*  optimization tool, but we may need to use it to match glibc prototypes. */
+#ifndef __THROW    /* I guess we're not on a glibc system */
+# define __THROW   /* __THROW is just an optimization, so ok to make it "" */
+#endif
+
+/*  Define the version number so folks can check against it */
+#define TC_VERSION_MAJOR  1
+#define TC_VERSION_MINOR  6
+#define TC_VERSION_PATCH  ""
+#define TC_VERSION_STRING "google-perftools 1.6"
+
+#include <stdlib.h>   /*  for struct mallinfo, if it's defined */
+
+/*  Annoying stuff for windows -- makes sure clients can import these functions */
+#ifndef PERFTOOLS_DLL_DECL
+# ifdef _WIN32
+#   define PERFTOOLS_DLL_DECL  __declspec(dllimport)
+# else
+#   define PERFTOOLS_DLL_DECL
+# endif
+#endif
+
+#ifdef __cplusplus
+#include <new>          /*  for std::nothrow_t */
+
+extern "C" {
+#endif
+  /*  Returns a human-readable version string.  If major, minor, */
+  /*  and/or patch are not NULL, they are set to the major version, */
+  /*  minor version, and patch-code (a string, usually ""). */
+  PERFTOOLS_DLL_DECL const char* tc_version(int* major, int* minor,
+                                            const char** patch) __THROW;
+
+  PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW;
+  PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW;
+  PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW;
+  PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) __THROW;
+  PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW;
+
+  PERFTOOLS_DLL_DECL void* tc_memalign(size_t __alignment,
+                                       size_t __size) __THROW;
+  PERFTOOLS_DLL_DECL int tc_posix_memalign(void** ptr,
+                                           size_t align, size_t size) __THROW;
+  PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) __THROW;
+  PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) __THROW;
+
+  PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW;
+  PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW;
+#if 0
+  PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW;
+#endif
+
+  /*  This is an alias for MallocExtension::instance()->GetAllocatedSize(). */
+  /*  It is equivalent to */
+  /*     OS X: malloc_size() */
+  /*     glibc: malloc_usable_size() */
+  /*     Windows: _msize() */
+  size_t tc_malloc_size(void* ptr) __THROW;
+
+#ifdef __cplusplus
+  PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW;
+  PERFTOOLS_DLL_DECL void* tc_new(size_t size);
+  PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size,
+                                          const std::nothrow_t&) __THROW;
+  PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p,
+                                            const std::nothrow_t&) __THROW;
+  PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
+  PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size,
+                                               const std::nothrow_t&) __THROW;
+  PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p,
+                                                 const std::nothrow_t&) __THROW;
+}
+#endif
+
+#endif  /*  #ifndef TCMALLOC_TCMALLOC_H_ */

kvastore/z_helpers.c

+#include "z_helpers.h"
+
+char* s_recv (void* socket) {
+  zmq_msg_t message;
+  int size;
+  char *string;
+
+  zmq_msg_init (&message);
+  if (zmq_recv (socket, &message, 0))
+    exit (1);           /*   Context terminated, exit */
+
+  string = malloc (size + 1);
+  size = zmq_msg_size (&message);
+  memcpy (string, zmq_msg_data (&message), size);
+  zmq_msg_close (&message);
+  string [size] = 0;
+  return string;
+}
+
+/*   Convert C string to 0MQ string and send to socket */
+int s_send (void* socket, char* string) {
+  int rc;
+  zmq_msg_t message;
+  zmq_msg_init_size (&message, strlen (string));
+  memcpy (zmq_msg_data (&message), string, strlen (string));
+  rc = zmq_send (socket, &message, 0);
+  assert (!rc);
+  zmq_msg_close (&message);
+  return (rc);
+}
+
+/*   Sends string as 0MQ string, as multipart non-terminal */
+int s_sendmore (void* socket, char* string) {
+  int rc;
+  zmq_msg_t message;
+  zmq_msg_init_size (&message, strlen (string));
+  memcpy (zmq_msg_data (&message), string, strlen (string));
+  rc = zmq_send (socket, &message, ZMQ_SNDMORE);
+  zmq_msg_close (&message);
+  assert (!rc);
+  return rc;
+}
+
+/*   Receives all message parts from socket, prints neatly */
+void s_dump (void* socket) {
+  puts ("----------------------------------------");
+  while (1) {
+    /*   Process all parts of the message */
+    zmq_msg_t message;
+    char *data = zmq_msg_data (&message);
+    int size = zmq_msg_size (&message);
+    int is_text = 1;
+    int char_nbr;
+    int64_t more;           /*   Multipart detection */
+    size_t more_size = sizeof (more);
+
+    zmq_msg_init (&message);
+    zmq_recv (socket, &message, 0);
+
+    /*   Dump the message as text or binary */
+    for (char_nbr = 0; char_nbr < size; char_nbr++)
+      if ((unsigned char) data [char_nbr] < 32
+          ||  (unsigned char) data [char_nbr] > 127)
+        is_text = 0;
+
+    printf ("[%03d] ", size);
+    for (char_nbr = 0; char_nbr < size; char_nbr++) {
+      if (is_text)
+        printf ("%c", data [char_nbr]);
+      else
+        printf ("%02X", (unsigned char) data [char_nbr]);
+    }
+    printf ("\n");
+
+    zmq_getsockopt (socket, ZMQ_RCVMORE, &more, &more_size);
+    zmq_msg_close (&message);
+    if (!more)
+      break;      /*   Last message part */
+  }
+}
+
+/*   Set simple random printable identity on socket */
+void s_set_id (void *socket) {
+  char identity [10];
+  sprintf (identity, "%04X-%04X", within (0x10000), within (0x10000));
+  zmq_setsockopt (socket, ZMQ_IDENTITY, identity, strlen (identity));
+}
+
+/*   Report 0MQ version number */
+void s_version (void) {
+  int major, minor, patch;
+  zmq_version (&major, &minor, &patch);
+  printf ("Current 0MQ version is %d.%d.%d\n", major, minor, patch);
+}

kvastore/z_helpers.h

+/*  =========================================================================
+    zhelpers.h
+
+    Helper header file for example applications.
+
+    -------------------------------------------------------------------------
+    Copyright (c) 1991-2010 iMatix Corporation <www.imatix.com>
+    Copyright other contributors as noted in the AUTHORS file.
+
+    This file is part of the ZeroMQ Guide: http://zguide.zeromq.org
+
+    This is free software; you can redistribute it and/or modify it under the
+    terms of the GNU Lesser General Public License as published by the Free
+    Software Foundation; either version 3 of the License, or (at your option)
+    any later version.
+
+    This software is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABIL-
+    ITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+    Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program. If not, see <http://www.gnu.org/licenses/>.
+    =========================================================================
+*/
+
+#ifndef __ZHELPERS_H_INCLUDED__
+#define __ZHELPERS_H_INCLUDED__
+
+#include <zmq.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+/* Bring Windows MSVC up to C99 scratch */
+#if (defined (__WINDOWS__))
+typedef unsigned long ulong;
+typedef unsigned int  uint;
+typedef __int64 int64_t;
+#endif
+
+/*   Provide random number from 0..(num-1) */
+#define within(num) (int) ((float) (num) * random () / (RAND_MAX + 1.0))
+
+/*   Receive 0MQ string from socket and convert into C string */
+/*   Caller must free returned string. */
+char* s_recv (void*);
+int s_send (void* , char*);
+int s_sendmore (void*, char*);
+void s_dump (void*);
+void s_set_id (void* );
+void s_version (void);
+
+#endif