2016/03/17


MySQLでカジュアルにズンドコキヨシ | GMOメディア エンジニアブログ

ズンドコキヨシ大流行ですね。 ズンドコキヨシまとめ - Qiita を見ていたんですが、MySQLでやってる人はいなさそうなので...

http://tech.gmo-media.jp/post/141178318699/zundoko-kiyoshi-with-mysql

SQLite3 でもやりたい!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sqlite3.h>
#include <sqlite3ext.h>

#ifdef _WIN32
# define EXPORT __declspec(dllexport)
#else
# define EXPORT
#endif

SQLITE_EXTENSION_INIT1;

static int
zundoko_connect(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **c) {
  char buf[256];
  snprintf(buf, sizeof(buf)-1"CREATE TABLE %s(val text)", argv[0]);
  int rc = sqlite3_declare_vtab(db, buf);
  *ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab));
  memset(*ppVTab, 0sizeof(sqlite3_vtab));
  return rc;
}

static int
zundoko_create(sqlite3 *db, void *pAux, int argc, const char * const * argv, sqlite3_vtab **ppVTab, char **c) {
  return zundoko_connect(db, pAux, argc, argv, ppVTab, c);
}

static int zundoko_disconnect(sqlite3_vtab *pVTab) {
  sqlite3_free(pVTab);
  return SQLITE_OK;
}

static int
zundoko_destroy(sqlite3_vtab *pVTab) {
  sqlite3_free(pVTab);
  return SQLITE_OK;
}

typedef struct {
  sqlite3_vtab_cursor base;
  int rowid;
  int kiyoshi;
  char zundoko[6];
} cursor;

static int
zundoko_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
  cursor *c = (cursor *)sqlite3_malloc(sizeof(cursor));
  *ppCursor = &c->base;
  return SQLITE_OK;
}

static int
zundoko_close(cursor *c) {
  sqlite3_free(c);
  return SQLITE_OK;
}

static int
zundoko_next(cursor *c) {
  int i, l = sizeof(c->zundoko)/sizeof(c->zundoko[0]);
  for (i = 0; i < l-1; i++)
    c->zundoko[i] = c->zundoko[i+1];
  c->zundoko[5] = c->kiyoshi ? 'K' : (rand() % 2 == 0 ? 'Z' : 'D');
  c->rowid++;
  return SQLITE_OK;
}

static int
zundoko_filter(cursor *c, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
  srand((unsigned)time(NULL));
  c->rowid = 0;
  c->kiyoshi = 0;
  memset(c->zundoko, 0sizeof(c->zundoko));
  zundoko_next(c);
  return SQLITE_OK;
}

static int
zundoko_eof(cursor *c) {
  c->kiyoshi = memcmp(c->zundoko+1"ZZZZD"5) == 0;
  return c->zundoko[4] == 'K';
}

static int
zundoko_column(cursor *c, sqlite3_context *ctxt, int i) {
  char *p = "?";
  switch (c->zundoko[5]) {
    case 'Z': p = "ズン"break;
    case 'D': p = "ドコ"break;
    case 'K': p = "キ・ヨ・シ!"break;
  }
  sqlite3_result_text(ctxt, strdup(p), strlen(p), NULL);
  return SQLITE_OK;
}

static int
zundoko_rowid(cursor *c, sqlite3_int64 *pRowid) {
  *pRowid = c->rowid;
  return SQLITE_OK;
}

static int
zundoko_bestindex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
  return SQLITE_OK;
}

static const sqlite3_module module = {
  0,
  zundoko_create,
  zundoko_connect,
  zundoko_bestindex,
  zundoko_disconnect,
  zundoko_destroy,
  zundoko_open,
  (int (*)(sqlite3_vtab_cursor *)) zundoko_close,
  (int (*)(sqlite3_vtab_cursor *, intchar const *, int, sqlite3_value **)) zundoko_filter,
  (int (*)(sqlite3_vtab_cursor *)) zundoko_next,
  (int (*)(sqlite3_vtab_cursor *)) zundoko_eof,
  (int (*)(sqlite3_vtab_cursor *, sqlite3_context *, int)) zundoko_column,
  (int (*)(sqlite3_vtab_cursor *, sqlite3_int64 *)) zundoko_rowid,
  NULL// zundoko_update
  NULL// zundoko_begin
  NULL// zundoko_sync
  NULL// zundoko_commit
  NULL// zundoko_rollback
  NULL// zundoko_findfunction
  NULL// zundoko_rename
};

static void
destructor(void *arg) {
  return;
}


EXPORT int
sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
  SQLITE_EXTENSION_INIT2(api);
  sqlite3_create_module_v2(db, "zundoko", &module, NULL, destructor);
  return 0;
}

こんなソースファイルを用意し Windows であれば以下の様にコンパイルします。

gcc -I. -g -o zundoko.dll -shared zundoko.c

sqlite3 を起動して dll を読み込みます。

SQLite version 3.7.14 2012-09-03 15:42:36
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

sqlite> select load_extension("zundoko.dll");
load_extension("zundoko.dll")

読み込めたら仮想テーブルを作ります。

sqlite> create virtual table zundoko using zundoko(val);

あとは SELECT

sqlite> SELECT * FROM ZUNDOKO;
ドコ
ドコ
ズン
ズン
ズン
ズン
ドコ
キ・ヨ・シ!

ソースコードはここに置いておきます。

Posted at by



2015/11/27


ちょっとしたツールを作る際にシングルヘッダのライブラリは非常に役立ちます。json を STL ライクに扱える picojson 等が有名ですね。

今日は GitHub で mmx というシングルヘッダライブラリを見つけました。

vurtun/mmx - GitHub

single header libraries for C/C++

https://github.com/vurtun/mmx

同梱されているライブラリは、JSON パーサ、C言語っぽい字句解析器、マルチスレッドスケジューラ、行列演算、ライトウェイトなウェブサーバです。

今日はこれを使ってファイルをサーブする簡単なウェブサーバを書いてみました。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#define MMW_STATIC
#define MMW_IMPLEMENTATION
#define MMW_USE_FIXED_TYPES
#define MMW_USE_ASSERT
#include "mm_web.h"

#ifdef _WIN32
#include <winsock2.h>
#define SERVER_INIT { \
  WORD wsa_version = MAKEWORD(2,2); \
  WSADATA wsa_data; \
  if (WSAStartup(wsa_version, &wsa_data)) { \
    fprintf(stderr"WSAStartup failed\n"); \
    return 1; \
  } \
}
#define SERVER_FINISH \
  WSACleanup();
#else
#define SERVER_INIT
#define SERVER_FINISH
#endif

#ifdef __APPLE__
#include <unistd.h>
#endif

static void
logger(const char* text) {
  printf("[debug] %s\n", text);
}

static int
dispatch(struct mmw_con *conn, void *userdata) {
  FILE* fp;
  char buf[4096];
  struct stat fi;

  strcpy(buf, "./public");
  strcat(buf, conn->request.uri);
  if (buf[strlen(buf)-1] == '/') strcat(buf, "index.html");
  if (stat(buf, &fi)) return 1;
  fp = fopen(buf, "r");
  if (!fp) return 1;
  mmw_response_begin(conn, 200, (int) fi.st_size, NULL0);
  while (!feof(fp)) {
    int n = fread(buf, 1sizeof buf, fp);
    mmw_write(conn, buf, n);
  }
  mmw_response_end(conn);
  fclose(fp);
  return 0;
}

int
main(void) {
  void *memory = NULL;
  mmw_size needed_memory = 0;
  struct mmw_server server;

  struct mmw_config config;
  memset(&config, 0sizeof config);
  config.address = "127.0.0.1";
  config.port = 8888;
  config.connection_max = 4;
  config.request_buffer_size = 2048;
  config.io_buffer_size = 8192;
  config.log = logger;
  config.dispatch = dispatch;

  SERVER_INIT;

  mmw_server_init(&server, &config, &needed_memory);
  memory = calloc(needed_memory, 1);
  mmw_server_start(&server, memory);
  while (1) mmw_server_update(&server);
  mmw_server_stop(&server);
  free(memory);

  SERVER_FINISH;

  return 0;
}

お決まりの手順がならびますが、ライトウェイトとは言えどある程度カスタマイズが可能なウェブサーバが作れそうです。パフォーマンスを計測してみようと思ったのですが、どこかで刺さって完走できませんでした。ただ落ちるといった事はありませんし、ブラウザで閲覧する限りにはちゃんと動作している様です。

ちょろっとファイルサーバを書きたい、といったニーズには便利そうです。

ライセンスは mm_json.h、mm_lexer.h、mm_sched.h、mm_vec.h が zlib ライセンス、mm_web.h が BSD ライセンスになります。

Posted at by



2015/10/14


Lepton という数式パーサを見つけたので遊んでみた。

Simtk.org: Lepton Mathematical Expression Parser: Overview

Purpose/Synopsis: A small C++ library for parsing, evaluating, differentiating, and analyzing mathem...

https://simtk.org/home/lepton

一般的な数式パーサは expression を token に分解するのみだが、この lepton はもう1歩踏み込んだ処理が行える。

通常の数式は以下の様にパースし実行出来る。

#include <iostream>
#include <string>
#include <map>
#include <Lepton.h>

int
main(int argc, char* argv[]) {
  std::map<std::string, double> variables;
  variables["x"] = 2.0;
  variables["y"] = 3.0;
  std::cout << Lepton::Parser::parse("x^y").evaluate(variables) << std::endl; 
  // 8
  return 0;
}

答えは8となる。もちろん定数も扱える。定数は optimize により実行前に展開される。例えば 2*3*xoptimize により 6*x にコンパイルされる。

#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <algorithm>
#include <Lepton.h>

#define PI 3.14

int
main(int argc, char* argv[]) {
  std::map<std::string, double> constants;
  constants["pi"] = PI;

  Lepton::ParsedExpression exp = Lepton::Parser::parse("2*pi*x").optimize(constants);

  std::cout << exp << std::endl;
  // 6.28319*(x) ;

  std::map<std::string, double> variables;
  variables["x"] = 2;
  std::cout << exp.evaluate(variables) << std::endl;
  // 12.56
  return 0;
}

また変数の参照も扱える。

#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <algorithm>
#include <Lepton.h>

int
main(int argc, char* argv[]) {
  Lepton::CompiledExpression expression =
    Lepton::Parser::parse("x+y^2").createCompiledExpression();
  double& x = expression.getVariableReference("x");
  double& y = expression.getVariableReference("y");
  x = 1;
  y = 2;
  std::cout << expression.evaluate() << std::endl;
  // 5
  return 0;
}

尚、実行時に変数が解決されていないと例外が発生する様になっている。

さらに関数が定義出来るので sin を定義してみた。

#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <algorithm>
#include <Lepton.h>

class SinFunc : public Lepton::CustomFunction {
  int getNumArguments() const {
    return 1;
  }
  Lepton::CustomFunction* clone() const {
    return new SinFunc();
  }
  double evaluate(const double* arguments) const {
    return std::sin(arguments[0]);
  }
  double evaluateDerivative(const double* arguments, const int*derivOrder) const {
    return evaluate(arguments);
  }
};

int
main(int argc, char* argv[]) {
  std::map<std::string, Lepton::CustomFunction*> functions;
  SinFunc sin_func;
  functions["sin"] = &sin_func;
  Lepton::ParsedExpression exp = Lepton::Parser::parse("sin(Θ)", functions);
  std::map<std::string, double> variables;
  variables["Θ"] = 3.14 / 2;
  std::cout << exp.evaluate(variables) << std::endl;
  // 1
  return 0;
}

とても便利そうです。これの文字列が扱える物があれば FizzBuzz とか出来て便利(ではないが)そうと思ってしまい、我ながら駄目な人間だなーと思った。

ライセンスはMITです。

Posted at by