d pacman按日期包装

pacman按日期包装

paks.hs
module Main where

import Data.Maybe
import Data.List
import Data.List.Split
import System.Process
import Data.Time.Parse
import Data.Time.LocalTime
import Data.Time.ISO8601

import qualified Data.Text as T

----------------

coalesce :: (a -> a -> Maybe a) -> [a] -> [a]
coalesce f x =
    case x of
      (p:q:x') ->
          case f p q of
            Nothing -> p : coalesce f (q : x')
            Just r -> coalesce f (r : x')
      _ -> x

fromRight :: Either a b -> b
fromRight (Right x) = x

strip :: String -> String
strip = T.unpack . T.strip . T.pack

----------------

type Entry = (String, String)

type Package = [Entry]

pacmanQueryInfoRaw :: IO String
pacmanQueryInfoRaw = readProcess "pacman" ["-Qi"] []

pacmanQueryInfo :: IO [Package]
pacmanQueryInfo = parsePackageList <$> pacmanQueryInfoRaw

parsePackageList :: String -> [Package]
parsePackageList input = map parsePackage blocks
  where
    blocks = endBy "\n\n" input

parsePackage :: String -> Package
parsePackage input = map fromRight $ coalesce joiner entries
  where
    inputLines = lines input
    entries    = map parsePackageEntry inputLines
    
    joiner (Right (title, value)) (Left value') = Just $ Right (title, value ++ " " ++ value')
    joiner _ _ = Nothing

parsePackageEntry :: String -> Either String Entry
parsePackageEntry input =
  case break (== ':') input of
    (value, [])        -> Left $ strip value
    (title, (_:value)) -> Right (strip title, strip value)

packageEntryValue :: String -> Package -> String
packageEntryValue key = fromJust . lookup key

packageName :: Package -> String
packageName = packageEntryValue "Name"

packageVersion :: Package -> String
packageVersion = packageEntryValue "Version"

packageInstallDate :: Package -> String
packageInstallDate = formatPackageDate . packageEntryValue "Install Date"

formatPackageDate :: String -> String
formatPackageDate input = formatISO8601 $ localTimeToUTC utc time
  where
    Just (time, _) = strptime "%a %d %b %Y %I:%M:%S %p" input

formatPackage :: Package -> String
formatPackage package = intercalate " " [installDate, name, version]
  where
    name        = packageName package
    version     = packageVersion package
    installDate = packageInstallDate package

main = do { packages <- pacmanQueryInfo
          ; putStrLn $ unlines $ map formatPackage packages
          }
paks.d
import std.stdio;
import std.process;
import std.string;
import std.range;
import std.algorithm;
import std.typecons;
import core.sys.posix.time;

bool notNullOrEmpty(T)(T s)
{
  return s != null || !s.empty;
}

bool notNull(T)(T s)
{
  return s != null;
}

auto dict(alias kf, alias vf, T)(T items)
{
  alias TK = typeof(kf(items.front));
  alias TV = typeof(vf(items.front));
  
  TV[TK] result;
  
  foreach (item; items)
  {
    result[kf(item)] = vf(item);
  }
  
  return result;
}

string[string][] getPacmanPackages()
{
  struct Property
  {
    string key, value;
  }
  
  auto parseProperty(string line)
  {
    auto ix = line.indexOf(':');
    if (ix == -1) return null;
    
    auto k = line[0..ix].strip;
    auto v = line[ix+1..$].strip;
    
    return new Property(k, v);
  }
  
  auto parsePackage(string lines)
  {
    return
      lines
      .splitter('\n')
      .map!parseProperty
      .filter!notNull
      .dict!(x => x.key, x => x.value);
  }
  
  return
    ["pacman", "-Qi"]
    .execute
    .output
    .splitter("\n\n")
    .filter!notNullOrEmpty
    .map!parsePackage
    .array;
}

auto fmtDateTime(string s)
{
  tm time;
  
  strptime(s.ptr, "%a %d %b %Y %I:%M:%S %p %Z", &time);
  
  char buf[30];
  
  auto len = strftime(buf.ptr, buf.length, "%Y-%m-%dT%H:%M:%S%Z", &time);
  
  auto fmt = buf[0..len].idup;
  
  return fmt;
}

void main()
{
  auto pks = getPacmanPackages();
  
  foreach (pk; pks)
  {
    writeln(pk["Install Date"].fmtDateTime, ' ', pk["Name"], ' ', pk["Version"]);
  }
}

d 用于从共享库加载导出函数的实用程序。

用于从共享库加载导出函数的实用程序。

example.d
// Windows only
version( Windows )
{
	enum WINDOWS = true;
}
else
{
	enum WINDOWS = false;
}

static if( !WINDOWS )
	static assert( false, "Cannot compile on non-Windows platforms" );

import dylib : DynamicLoader;
import std.conv : toStringz;

void main()
{
	// call using loadFunction
	auto loader = new DynamicLoader( "User32.dll" );
	auto MessageBoxA = loader.loadFunction!( "MessageBoxA", int, void*, const char*, const char*, uint );
	
	MessageBoxA( null, "Hello from D".toStringz, "Test".toStringz, 0 );
	
	// using opDispatch
	auto User32 = loader;
	
	int result;
	User32.MessageBoxA( result, cast(void*)null, "Hello from D".toStringz, "Test".toStringz, 0u );
	
}
dylib.d
module dylib;

private {
    import std.string    : format, toStringz;
    import std.exception : enforce;

    version( Windows )
    {
        extern( Windows ) void* LoadLibraryA( const char* );
        extern( Windows ) void* GetProcAddress( void*, const char* );
        extern( Windows ) void  FreeLibrary( void* );

        void* loadLibrary( string name )
        {
            return LoadLibraryA( name.toStringz );
        }

        void* loadSymbol( void* handle, string name )
        {
            return GetProcAddress( handle, name.toStringz );
        }

        alias freeLibrary = FreeLibrary;
    }
    else version( linux )
    {
        extern( C ) void* dlopen( const char*, int );
        extern( C ) void* dlsym( void*, const char* );
        extern( C ) void  dlclose( void* );

        void* loadLibrary( string name )
        {
            return dlopen( name.toStringz, 2 );
        }

        void* loadSymbol( void* handle, string name )
        {
            return dlsym( handle, name.toStringz );
        }

        alias freeLibrary = dlclose;
    }
    else
    {
        static assert( false, "Unsupported OS" );
    }
}

final class InteropException : Exception
{
    this( string msg, string file = __FILE__, size_t line = __LINE__ )
    {
        super( msg, file, line );
    }
}

final class DynamicLoader
{
    private void* library;
    private void*[string] symbols;

    this( string path )
    {
        this.library = enforce!( InteropException )( loadLibrary( path ), "Unable to load library '%s'".format( path ) );
    }

    ~this()
    {
        freeLibrary( this.library );
    }

    auto loadFunction( string name, TRet = void, TArgs... )()
    {
        alias func_t = extern( C ) TRet function( TArgs );
        void* symbol = this.loadSymbol!name;

        return cast(func_t)symbol;
    }

    void opDispatch( string name, TRet, TArgs... )( out TRet result, TArgs args )
    {
        auto func = this.loadFunction!( name, TRet, TArgs );
        result = func( args );
    }

    private void* loadSymbol( string name )()
    {
        if( auto ptr = name in this.symbols )
            return *ptr;

        auto symbol = enforce!( InteropException )( .loadSymbol( this.library, name ), "Unable to load symbol '%s'".format( name ) );
        this.symbols[name] = symbol;

        return symbol;
    }
}

d 拨打Twitter APIたたくやつ(https://github.com/k3kaimu/graphite/blob/master/source/graphite/twitter/api.dの作り直し)

拨打Twitter APIたたくやつ(https://github.com/k3kaimu/graphite/blob/master/source/graphite/twitter/api.dの作り直し)

shrine12.d
// Written in the D programming language.
/*
NYSL Version 0.9982

A. This software is "Everyone'sWare". It means:
  Anybody who has this software can use it as if he/she is
  the author.

  A-1. Freeware. No fee is required.
  A-2. You can freely redistribute this software.
  A-3. You can freely modify this software. And the source
      may be used in any software with no limitation.
  A-4. When you release a modified version to public, you
      must publish it with your name.

B. The author is not responsible for any kind of damages or loss
  while using or misusing this software, which is distributed
  "AS IS". No warranty of any kind is expressed or implied.
  You use AT YOUR OWN RISK.

C. Copyrighted to Kazuki KOMATSU

D. Above three clauses are applied both to source and binary
  form of this software.
*/

/**
このモジュールでは、Twitter-APIを叩きます。

Thanks: http://qiita.com/woxtu/items/9656d426f424286c6571
*/
module shrine12;

import std.algorithm;
import std.array;
import std.ascii;
import std.base64;
import std.conv;
import std.datetime;
import std.format;
import std.json;
import std.net.curl;
import std.path;
import std.range;
import std.regex;
import std.uni;
import std.utf;
import std.uri;

/*
from http://qiita.com/mono_shoo/items/47ae6011faed6ee78334
*/
private
JSONValue twToJSONValue(string content)
{
    enum r = ctRegex!r"\\u([0-9a-fA-F]{4})";

    static string func(Captures!string c)
    {
        dchar val = 0;
        char[4] buf = void;
        auto str = c[1].toUpper();

        foreach (i, ch; str)
            val += (isDigit(ch) ? ch - '0' : ch - ('A' - 10)) << (4 * (3-i));

        return isValidDchar(val) ? toUTF8(buf, val).idup : "□";
    }

    return content.replaceAll!func(r).parseJSON();
}


/*
encodeComponent for twitter
*/
private
void twEncodeComponent(string tw, ref string dst)
{
    /*
    enum re = ctRegex!`[\*"'\(\)!]`;

    static string func(T)(T m){
        char c = m.hit[0];
        return format("%%%X", c);
    }

    return tw.encodeComponent.replaceAll!func(re);
    */
    tw = tw.encodeComponent;

    foreach(char c; tw) switch(c) {
      case '*', '"', '\'', '(', ')', '!':
        dst ~= format("%%%X", c);
      default:
        dst ~= c;
    }
}


private
string twDecodeHTMLEntity(string str)
{
    return str.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&");
}


/**
URL query encoder for twitter.
*/
struct URLQuery
{
    string[string] queries;
    alias queries this;


    /**
    add query
    */
    void add(string param, string value)
    {
        queries[param] = value;
    }


    /// ditto
    void opIndexAssign(string value, string key)
    {
        this.add(key, value);
    }


    /**
    encode to url query
    */
    void encode(W)(ref W writer) const
    if(isOutputRange!(W, string) && isOutputRange!(W, char))
    {
        auto kvs = queries.byKeyValue.array();
        kvs.sort!"a.key < b.key"();

        foreach(i, const ref e; kvs){
            if(i != 0)
                .put(writer, '&');

            string pair;
            twEncodeComponent(e.key, pair);
            pair ~= '=';
            twEncodeComponent(e.value, pair);
            .put(writer, pair);
        }
    }


    /**
    */
    string encode() const @property
    {
        auto app = appender!string();

        this.encode(app);

        return app.data;
    }


    /**

    */
    URLQuery opBinary(string op: "~")(in URLQuery other) const
    {
        auto cpy = this.dup;
        cpy ~= other;
        return cpy;
    }


    void opOpAssign(string op: "~")(in URLQuery other)
    {
        foreach(k, v; other.queries)
            queries[k] = v;
    }


    /**

    */
    URLQuery dup() const pure @safe @property
    {
        string[string] cpy;
        foreach(k, v; this.queries)
            cpy[k] = v;

        return URLQuery(cpy);
    }
}


/**

*/
struct KeySecret
{
    string key, secret;
}


/**

*/
struct OAuthToken
{
    KeySecret consumer;
    KeySecret* access;
}


private
KeySecret splitToken(string s)
{
    KeySecret ks;

    foreach(x; s.split('&').map!`a.split('=')`){
        if(x[0] == "oauth_token")
            ks.key = x[1];
        else if(x[0] == "oauth_token_secret")
            ks.secret = x[1];
    }

    return ks;
}


struct TwitterAPI
{
    OAuthToken token;


    string setRequestToken()
    {
        auto reqTok = this.get(`https://api.twitter.com/oauth/request_token`, URLQuery(null)).splitToken();
        token.access = new KeySecret;
        *(token.access) = reqTok;
        writeln(reqTok);

        return this.authorizeRequstURL();
    }


    void authorize(string verifier)
    {
        *(token.access) = this.get(`https://api.twitter.com/oauth/access_token`, URLQuery(["oauth_verifier" : verifier])).splitToken();
    }


  const:
    string signature(string method, string url, in URLQuery queries)
    {
        string key;
        {
            twEncodeComponent(this.token.consumer.secret, key);
            key ~= '&';

            if(this.token.access !is null)
                twEncodeComponent(this.token.access.secret, key);
        }

        string msg;
        {
            auto msgApp = appender!string();
            msgApp.put(method);
            msgApp.put('&');
            msgApp.put(url.encodeComponent);
            msgApp.put('&');
            msg = msgApp.data;

            queries.encode().twEncodeComponent(msg);
        }

        return Base64.encode(hmacOf!SHA1(key, msg)[]);
    }


    string oauthHTTPAuthorization(string method, string url, in URLQuery queries)
    {
        URLQuery uq;
        immutable timestamp = Clock.currTime.toUnixTime.to!string;

        uq["oauth_consumer_key"] = this.token.consumer.key;
        uq["oauth_nonce"] = timestamp;
        uq["oauth_signature_method"] = "HMAC-SHA1";
        uq["oauth_timestamp"] = timestamp;
        uq["oauth_version"] = "1.0";

        if(token.access !is null)
            uq["oauth_token"] = token.access.key;

        uq["oauth_signature"] = signature(method, url, uq ~ queries);
        return "OAuth " ~ uq.encode().replace("&", ",");
    }


    Return signedCall(Return)(string method,
                              string url,
                              in URLQuery queries,
                              Return delegate(HTTP http, string url, in URLQuery queries) dlg)
    {
        auto http = HTTP();
        http.verifyPeer(true);
        http.caInfo = `cacert.pem`;
        http.addRequestHeader("Authorization", oauthHTTPAuthorization(method, url, queries));
        return dlg(http, url, queries);
    }


    string get(string url, in URLQuery queries)
    {
        return signedCall("GET", url, queries,
            delegate(HTTP http, string url, in URLQuery qs)
            {
                return std.net.curl.get(qs.length ? url ~ "?" ~ qs.encode(): url, http).idup;
            }
        );
    }


    string post(string url, in URLQuery queries)
    {
        return signedCall("POST", url, queries,
            delegate(HTTP http, string url, in URLQuery queries)
            {
                return std.net.curl.post(url, queries.encode(), http).idup;
            }
        );
    }


    string postImage(string url, string endPoint, in string[] filenames, in URLQuery queries)
    {
        return signedCall("POST", url, URLQuery(null),
            delegate(HTTP http, string url, in URLQuery /*qs*/)
            {
                immutable boundary = `cce6735153bf14e47e999e68bb183e70a1fa7fc89722fc1efdf03a917340`;   // 適当な文字列
                http.addRequestHeader("Content-Type", "mutipart/form-data; boundary=" ~ boundary);

                auto app = appender!string();
                foreach(k, v; queries){
                    app.formattedWrite("--%s\r\n", boundary);
                    app.formattedWrite(`Content-Disposition: form-data; name="%s"`"\r\n", k);
                    app.formattedWrite("\r\n");
                    app.formattedWrite("%s\r\n", v);
                }


                auto bin = appender!(const(ubyte)[])(cast(const(ubyte[]))app.data);
                foreach(e; filenames){
                    bin.put(cast(const(ubyte)[])format("--%s\r\n", boundary));
                    bin.put(cast(const(ubyte)[])format("Content-Type: application/octet-stream\r\n"));
                    bin.put(cast(const(ubyte)[])format(`Content-Disposition: form-data; name="%s"; filename="%s"`"\r\n", endPoint, e.baseName));
                    bin.put(cast(const(ubyte[]))"\r\n");
                    bin.put(cast(const(ubyte)[])std.file.read(e));
                    bin.put(cast(const(ubyte[]))"\r\n");
                }
                bin.put(cast(const(ubyte)[])format("--%s--\r\n", boundary));

                return std.net.curl.post(url, bin.data, http).idup;
            }
        );
    }


    /**
    各APIを叩くためのメソッドです
    */
    auto call(string name, T...)(auto ref T args) const
    {
        return mixin(`TwitterAPI.` ~ name ~ `(this, forward!args)`);
    }


    string authorizeRequstURL()
    {
        return `https://api.twitter.com/oauth/authorize?oauth_token=` ~ this.token.access.key;
    }


  public:
  static:
    struct account
    {
      static:
        auto settings(in TwitterAPI token)
        {
            return token.get(`https://api.twitter.com/1.1/account/settings.json`, URLQuery(null));
        }


        auto verifyCredentials(in TwitterAPI token, in URLQuery qs)
        {
            return token.get(`https://api.twitter.com/1.1/account/verify_credentials.json`, qs);
        }
    }


    struct statuses
    {
      static:
        auto mentionsTimeline(in TwitterAPI token, in URLQuery qs)
        {
            return token.get(`https://api.twitter.com/1.1/statuses/mentions_timeline.json`, qs);
        }


        auto userTimeline(in TwitterAPI token, in URLQuery qs)
        {
            return token.get(`https://api.twitter.com/1.1/statuses/user_timeline.json`, qs);
        }


        auto homeTimeline(in TwitterAPI token, in URLQuery qs)
        {
            return token.get(`https://api.twitter.com/1.1/statuses/home_timeline.json`, qs);
        }


        auto retweetsOfMe(in TwitterAPI token, in URLQuery qs)
        {
            return token.get(`https://api.twitter.com/1.1/statuses/retweets_of_me.json`, qs);
        }


        /**
        ツイートします。

        Example:
        ---------------------
        import std.array, std.format, std.json;

        // ツイート
        string tweet(Twitter tw, string msg)
        {
            return tw.callAPI!"statuses.update"(["status" : msg]);
        }


        // 画像も一緒にツイート
        string tweetWithMedia(Twitter tw, string msg, string[] imgFilePaths)
        {
            return tw.callAPI!"statuses.update"([
                "status" : msg,
                "media_ids" : format("%(%s,%)",
                                imgFilePaths.map!(a => parseJSON(tw.callAPI!"media.upload"(a))["media_id_string"].str))
            ]);
        }
        ---------------------
        */
        auto update(in TwitterAPI token, in URLQuery qs)
        {
            return token.post(`https://api.twitter.com/1.1/statuses/update.json`, qs);
        }


        /**
        画像1枚と一緒にツイート

        Example:
        ------------------------
        string tweetWithMedia(Twitter tw, string msg, string imgFilePath)
        {
            return tw.callAPI!"statuses.updateWithMedia"(imgFilePath, ["status" : msg]);
        }
        ------------------------
        */
        auto updateWithMedia(in TwitterAPI token, string filePath, in URLQuery qs)
        {
            string[1] filenames = [filePath];
            return token.postImage(`https://api.twitter.com/1.1/statuses/update_with_media.json`, "media[]", filenames, qs);
        }
    }


    struct media
    {
      static:
        /**
        画像をuploadします

        Example:
        -------------------------
        import std.json;

        // 画像をuploadして、画像のidを取得する
        string uploadImage(Twitter tw, string imgFilePath)
        {
            return parseJSON(tw.callAPI!"media.upload"(imgFilePath))["media_id_string"].str;
        }
        -------------------------
        */
        string upload(in TwitterAPI token, string filePath)
        {
            immutable url = `https://upload.twitter.com/1.1/media/upload.json`;
            string[1] filenames = [filePath];
            return token.postImage(url, "media", filenames, URLQuery(null));
        }
    }


    //struct userstream
    //{
    //  static:
    //    /**
    //    Userstreamに接続します
    //    */
    //    auto user(in TwitterAPI token, in URLQuery qs)
    //    {
    //        return this.streamGet(`https://userstream.twitter.com/1.1/user.json`, dur!"seconds"(5), qs);
    //    }
    //}
}



import std.digest.sha;
import std.digest.md;


/**

*/
struct HMAC(Hash)if(isDigest!Hash)
{
    this(const(ubyte)[] key) pure nothrow @safe
    {
        _ipad.length = blockSize;
        _opad.length = blockSize;

        if(key.length > blockSize){
            _hash.start();
            _hash.put(key);
            _key = _hash.finish()[];
        }else
            _key = key;

        if(_key.length < blockSize)
            _key.length = blockSize;

        foreach(i; 0 .. blockSize){
            _ipad[i] = _key[i] ^ 0x36;
            _opad[i] = _key[i] ^ 0x5c;
        }

        this.start();
    }


    void start() pure nothrow @safe
    {
        _hash.start();
        _hash.put(_ipad);
    }


    void put(scope const(ubyte)[] input...) pure nothrow @safe
    {
        _hash.put(input);
    }


    auto finish() pure nothrow @safe
    {
        auto inner = _hash.finish();

        _hash.put(_opad);
        _hash.put(inner[]);
        auto result = _hash.finish();
        
        _hash.put(_ipad);   // this.start();

        return result;
    }


  private:
    Hash _hash;
    const(ubyte)[] _key;
    ubyte[] _ipad;
    ubyte[] _opad;

    static if(is(Hash == std.digest.sha.SHA1) || is(Hash == std.digest.md.MD5))
        enum blockSize = 64;
    else 
        enum blockSize = Hash.blockSize;
}

unittest{
    // HMAC-MD5 test case : http://www.ipa.go.jp/security/rfc/RFC2202JA.html
    import std.algorithm, std.range, std.array, std.digest.digest;

    auto hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0x0b), 16)));
    put(hmac_md5, cast(ubyte[])"Hi There");
    assert(toHexString(hmac_md5.finish()) == "9294727A3638BB1C13F48EF8158BFC9D");

    hmac_md5 = HMAC!(MD5)(cast(ubyte[])"Jefe");
    put(hmac_md5, cast(ubyte[])"what do ya want for nothing?");
    assert(toHexString(hmac_md5.finish()) == "750C783E6AB0B503EAA86E310A5DB738");

    hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0xaa), 16)));
    put(hmac_md5, array(take(repeat(cast(ubyte)0xdd), 50)));
    assert(toHexString(hmac_md5.finish()) == "56BE34521D144C88DBB8C733F0E8B3F6");

    hmac_md5 = HMAC!(MD5)(array(map!"cast(ubyte)a"(iota(1, 26))));
    put(hmac_md5, array(take(repeat(cast(ubyte)0xcd), 50)));
    assert(toHexString(hmac_md5.finish()) == "697EAF0ACA3A3AEA3A75164746FFAA79");

    hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0x0c), 16)));
    put(hmac_md5, cast(ubyte[])"Test With Truncation");
    assert(toHexString(hmac_md5.finish()) == "56461EF2342EDC00F9BAB995690EFD4C");

    hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0xaa), 80)));
    put(hmac_md5, cast(ubyte[])"Test Using Larger Than Block-Size Key - Hash Key First");
    assert(toHexString(hmac_md5.finish()) == "6B1AB7FE4BD7BF8F0B62E6CE61B9D0CD");

    hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0xaa), 80)));
    put(hmac_md5, cast(ubyte[])"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
    assert(toHexString(hmac_md5.finish()) == "6F630FAD67CDA0EE1FB1F562DB3AA53E");
}

unittest{
    // HMAC-SHA1 test case : http://www.ipa.go.jp/security/rfc/RFC2202JA.html
    import std.algorithm, std.range, std.array, std.digest.digest;

    auto hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0x0b), 20)));
    put(hmac_sha1, cast(ubyte[])"Hi There");
    assert(toHexString(hmac_sha1.finish()) == "B617318655057264E28BC0B6FB378C8EF146BE00");

    hmac_sha1 = HMAC!(SHA1)(cast(ubyte[])"Jefe");
    put(hmac_sha1, cast(ubyte[])"what do ya want for nothing?");
    assert(toHexString(hmac_sha1.finish()) == "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79");

    hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0xaa), 20)));
    put(hmac_sha1, array(take(repeat(cast(ubyte)0xdd), 50)));
    assert(toHexString(hmac_sha1.finish()) == "125D7342B9AC11CD91A39AF48AA17B4F63F175D3");

    hmac_sha1 = HMAC!(SHA1)(array(map!"cast(ubyte)a"(iota(1, 26))));
    put(hmac_sha1, array(take(repeat(cast(ubyte)0xcd), 50)));
    assert(toHexString(hmac_sha1.finish()) == "4C9007F4026250C6BC8414F9BF50C86C2D7235DA");

    hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0x0c), 20)));
    put(hmac_sha1, cast(ubyte[])"Test With Truncation");
    assert(toHexString(hmac_sha1.finish()) == "4C1A03424B55E07FE7F27BE1D58BB9324A9A5A04");

    hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0xaa), 80)));
    put(hmac_sha1, cast(ubyte[])"Test Using Larger Than Block-Size Key - Hash Key First");
    assert(toHexString(hmac_sha1.finish()) == "AA4AE5E15272D00E95705637CE8A3B55ED402112");

    hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0xaa), 80)));
    put(hmac_sha1, cast(ubyte[])"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
    assert(toHexString(hmac_sha1.finish()) == "E8E99D0F45237D786D6BBAA7965C7808BBFF1A91");
}


/**

*/
auto hmacOf(Hash)(in void[] key, in void[] input)
{
    auto hash = HMAC!Hash(cast(const(ubyte)[])key);
    hash.put(cast(const(ubyte)[])input);
    return hash.finish;
}


void main()
{
    import std.stdio;
    import std.string;
    import std.process;

    TwitterAPI api;
    api.token.consumer.key = `*********************`;
    api.token.consumer.secret = `*********************`;

    //api.token.access = new KeySecret;
    //api.token.access.key = "*********************";
    //api.token.access.secret = "*********************";

    browse(api.setRequestToken());

    string pin = chomp(readln());
    api.authorize(pin);

    api.call!`statuses.update`(URLQuery(["status": "やっほー"]));
}

d SSEを使った复素数の内积

SSEを使った复素数の内积

bench.d
import core.simd;
import std.stdio;
import std.datetime;
import std.math;
import std.parallelism;
import std.range;
import std.random;


cfloat dotProduct(in Vector!(float[4])[] a, in Vector!(float[4])[] b)
in{
    assert(a.length == b.length);
}
body{
    Vector!(float[4]) r, q;
    r = 0;
    q = 0;

    auto px = a.ptr,
         ph = b.ptr,
         qx = a.ptr + a.length;

    while(px != qx)
    {
        Vector!(float[4]) x0 = *px,
                          h = *ph;
        r += x0 * h;

        x0 = __simd(XMM.SHUFPS, x0, x0, 0b10_11_00_01);

        q += x0 * h;

        ++px;
        ++ph;
    }

    Vector!(float[4]) sign, ones;
    sign.array = [1.0f, -1.0f, 1.0f, -1.0f];
    ones.array = [1.0f, 1.0f, 1.0f, 1.0f];

    r = __simd(XMM.DPPS, r, sign, 0b11111111);
    q = __simd(XMM.DPPS, q, ones, 0b11111111);

    return r.array[0] + q.array[0]*1i;
}


void main()
{
    enum size_t N = 1024;
    enum size_t Times = 1024*32;

    Vector!(float[4])[] xs = new Vector!(float[4])[N],
                        hs = xs.dup;

    cfloat[] cxs = (cast(cfloat*)xs.ptr)[0 .. N*2],
             chs = (cast(cfloat*)hs.ptr)[0 .. N*2];

    foreach(i, ref e; cxs)
        e = uniform01() + uniform01()*1i;

    foreach(i, ref e; chs)
        e = uniform01() + uniform01()*2i;

    cfloat[5] res;
    res[0] = 0+0i;
    res[1] = 0+0i;

    auto start = Clock.currTime;
    {
        foreach(times; 0 .. Times)
        {
            res[0] = 0+0i;
            foreach(i; 0 .. N*2)
                res[0] += cxs[i] * chs[i];
        }
    }
    auto bnch1 = Clock.currTime - start;

    start = Clock.currTime;
    {
        foreach(times; 0 .. Times)
        {
            res[1] = 0+0i;
            auto px = cxs.ptr,
                 qx = px + cxs.length,
                 ph = chs.ptr;

            while(px != qx)
            {
                res[1] += (*px) * (*ph);
                ++px;
                ++ph;
            }
        }
    }
    auto bnch2 = Clock.currTime - start;

    start = Clock.currTime;
    {
        foreach(times; 0 .. Times)
        {
            res[2] = 0+0i;
            auto px = cxs.ptr,
                 qx = px + cxs.length,
                 ph = chs.ptr;

            while(px != qx)
            {
                res[2] += *(px+0) * *(ph+0);
                res[2] += *(px+1) * *(ph+1);
                res[2] += *(px+2) * *(ph+2);
                res[2] += *(px+3) * *(ph+3);
                res[2] += *(px+4) * *(ph+4);
                res[2] += *(px+5) * *(ph+5);
                res[2] += *(px+6) * *(ph+6);
                res[2] += *(px+7) * *(ph+7);
                px += 8;
                ph += 8;
            }
        }
    }
    auto bnch3 = Clock.currTime - start;

    start = Clock.currTime;
    {
        foreach(times; 0 .. Times)
            res[3] = dotProduct(xs, hs);
    }
    auto bnch4 = Clock.currTime - start;

    start = Clock.currTime;
    {
        foreach(times; parallel(iota(0, Times)))
            res[4] = dotProduct(xs, hs);  // やばそうだけど無視
    }
    auto bnch5 = Clock.currTime - start;

    /*
    459[ms], 146.067[Msps]
    257[ms], 260.873[Msps]
    212[ms], 316.248[Msps]
    92[ms], 728.747[Msps]
    21[ms], 3192.77[Msps]
    -556.742+1556.79i == -556.742+1556.79i == -556.742+1556.79i == -556.741+1556.79i == -556.741+1556.79i
    */
    writefln("%s[ms], %s[Msps]", bnch1.total!"msecs", cxs.length * Times * 1.0 / (bnch1.total!"usecs"));
    writefln("%s[ms], %s[Msps]", bnch2.total!"msecs", cxs.length * Times * 1.0 / (bnch2.total!"usecs"));
    writefln("%s[ms], %s[Msps]", bnch3.total!"msecs", cxs.length * Times * 1.0 / (bnch3.total!"usecs"));
    writefln("%s[ms], %s[Msps]", bnch4.total!"msecs", cxs.length * Times * 1.0 / (bnch4.total!"usecs"));
    writefln("%s[ms], %s[Msps]", bnch5.total!"msecs", cxs.length * Times * 1.0 / (bnch5.total!"usecs"));
    writefln("%(%s == %)", res[]);
}

d PNG画像にバイナリデータを埋め込んだり,PNGに埋め込まれたバイナリデータを抽出するヤツ

PNG画像にバイナリデータを埋め込んだり,PNGに埋め込まれたバイナリデータを抽出するヤツ

bng.d
import imageformats;
import std.algorithm;
import std.conv;
import std.exception;
import std.file;
import std.math;
import std.stdio;
import std.string;


void main()
{
    ubyte[] dst = cast(ubyte[])`×ご検証
○ご健勝
`;
    encodeBng(dst, "output.png", 100);
    decodeBng("output.png", dst);
    std.file.write("output2.dat", dst);
}


void decodeBng(string pngFile, ref ubyte[] dst)
{
    auto img = read_png(pngFile, 4);
    enforce(img.c == ColFmt.RGBA);

    auto ps = img.pixels;
    immutable uint numBytes = (cast(uint[])(ps[0 .. 4].reverse(), ps))[0];

    dst.length = numBytes;
    dst[] = ps[4 .. 4 + numBytes];
}


void encodeBng(in ubyte[] data, string pngFile, size_t minWH = 0)
{
    immutable numPixels = (data.length + 4) / 4;
    size_t w = cast(size_t)sqrt(cast(real)numPixels);
    size_t h = numPixels / w;

    if(numPixels > w * h) ++w;

    if(w < minWH) w = minWH;
    if(h < minWH) h = minWH;

    auto buf = new ubyte[w * h * 4];

    buf[0 .. 4] = (a => (a.reverse(), a))(cast(ubyte[])([data.length].dup))[];
    buf[4 .. 4 + data.length] = data[];

    write_png(pngFile, w, h, buf, 4);
}

d bng.d

bng.d
import std.stdio;
import derelict.sdl2.sdl;
import derelict.sdl2.image;
import std.string;
import std.conv;
import std.exception;

static this()
{
    DerelictSDL2.load();
    DerelictSDL2Image.load();
}

void main()
{
    enforce(SDL_Init(SDL_INIT_VIDEO) >= 0);
    enforce(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG);

    auto input = readln().chomp();
    auto img = IMG_Load(input.toStringz());
    if(img is null)
        writeln(IMG_GetError().to!string);

    auto bs = cast(ubyte*)(img.pixels);
    size_t bytes = bs[0] << 24 | bs[1] << 16 | bs[2] << 8 | bs[3];

    File file = File("output.dat", "w");
    file.rawWrite(img.pixels[4 .. bytes + 4]);
}

d TeX的DSLの的ななにか

TeX的DSLの的ななにか

texdsl.d
import std.array;
import std.ascii;
import std.format;
import std.stdio;
import std.string;


void main()
{
    auto fig =
        TeX.Figure(["!t"], TeX.Center(TeX
            .includegraphics(["width=65mm"], "fig/foo.pdf").br
            .caption("なんかの図")
            .label("fig:foo")
        ));

    writeln(fig);
}


struct TeX 
{
  static:
    TeXValue opDispatch(string name)(string arg) { return opDispatch!name(null, arg); }

    TeXValue opDispatch(string name)(string[] opt, string arg)
    {
        auto app = appender!string();
        auto sub = appender!string();

        if(opt.length)
            sub.formattedWrite(`[%-(%s, %)]`, opt);

        if(name[0].isUpper)
        {
            app.formattedWrite(`\begin%1$s{%2$s}%3$s\end{%2$s}`, sub.data, name.toLower, arg);
        }
        else
        {
            app.formattedWrite(`\%1$s%2$s{%3$s}`, name, sub.data, arg);
        }

        return TeXValue(app.data);
    }
}


struct TeXValue
{
    this(string str)
    {
        _str = str;
    }


    string toString() const pure nothrow @safe @nogc @property { return _str; }
    alias toString this;

  const:
    TeXValue opDispatch(string name)(string arg) { return opDispatch!name(null, arg); }

    TeXValue opDispatch(string name)(string[] opt, string arg)
    if(isLower(name[0]))
    {
        auto app = appender!string();
        auto sub = appender!string();

        if(opt.length)
            sub.formattedWrite(`[%-(%s, %)]`, opt);

        app.put(_str);
        app.put(`\n`);

        switch(name)
        {
          default:
            app.formattedWrite(`\%1$s%2$s{%3$s}`, name, sub.data, arg);
        }

        return TeXValue(app.data);
    }


    TeXValue opDispatch(string name)() @property
    {
        auto app = appender!string();
        app.put(_str);

        switch(name)
        {
          case "br":
            app.put(`\\ `);

          default:
            app.formattedWrite(`\%1$s`, name);
        }

        return TeXValue(app.data);
    }

  private:
    string _str;
}

d Pandoc降价でからJSON経由で的TeXまで変换するスクリプト

Pandoc降价でからJSON経由で的TeXまで変换するスクリプト

docgen.d
import std.algorithm;
import std.array;
import std.conv;
import std.exception;
import std.json;
import std.string;
import std.traits;
import std.typecons;
import std.typetuple;
import std.variant;
import std.process;
import std.meta;
import std.file;
import std.stdio;
import std.path;
import pandoc;


void main(string[] args)
{
    auto mdFilename = args[1];
    auto fnameBody = mdFilename.stripExtension;

    auto txt = readText(mdFilename);
    auto json = txt.convertByPandoc(DocFormat.markdown, DocFormat.json);
    auto jsonValue = json.parseJSON();
    auto ast = jsonValue.fromJSONValue!Pandoc();
    ast.replaceIncludeBlock();  
    ast.evalInPlace();
    ast.sortBibitems("bibitems");

    auto transformedJSON = ast.toJSONValue().toJSONFormatString();
    std.file.write("transformedJSON.json", transformedJSON);
    //writeln(transformedJSON.convertByPandoc(DocFormat.json, DocFormat.latex, "--template=template.tex", ""));
    transformedJSON.convertByPandoc(DocFormat.json, DocFormat.latex, "--template=template.tex", "-o", fnameBody.setExtension(".tex"));

    {
        auto status = spawnShell(["/Applications/UpTeX.app/teTeX/bin/platex", fnameBody.setExtension(".tex")]).wait;
        if(status != 0){
            writeln("platex failed!");
            return;
        }
    }

    {
        auto status = spawnShell(["/Applications/UpTeX.app/teTeX/bin/dvipdfmx", fnameBody.setExtension(".dvi")]).wait;
        if(status != 0){
            writeln("dvipdfmx falied!");
            return;
        }
    }

    browse("file://" ~ absolutePath(fnameBody.setExtension(".pdf")));
}


void evalInPlace(ref Pandoc doc)
{
    auto dcode = appender!string(`
        import std.stdio, std.conv, std.json, std.file;
        import pandoc;
        void main(){
            JSONValue meta;
            {
                if(exists("meta.json")){
                    auto str = readText("meta.json");
                    meta = str.parseJSON();
                    meta["_meta_cache_"] = str;
                }
            }
            scope(success) std.file.write("docgen_jsongen.json", toJSONFormatString(meta));

            string[] _evaled__;`
            );

    static struct Evaled
    {
        void delegate(string[] evals) dg;
        size_t cnt;
    }

    Evaled[] evals;

    doc.walk!((ref a)
        {
            Inline[] fI(ref Inline[] arr)
            {
                bool[size_t] idxset;

                foreach(idx, ref a; arr){
                    if(auto p = a.content.peek!(Inline.Code)){
                        if(!p.attr.classes.find("dcode").empty){
                            dcode ~= `_evaled__ ~= to!string(`;
                            dcode ~= p.content;
                            dcode ~= `);`"\n";
                            idxset[idx] = true;
                        }
                    }
                }

                if(idxset.length == 0)
                    return arr;

                evals ~= Evaled(
                    (string[] evals){
                        Inline[] newInlines;
                        foreach(i, ref a; arr){
                            if(i in idxset){
                                auto p = a.content.peek!(Inline.Code)
                                         .enforce("Unknown error");

                                if(!p.attr.classes.find("emplace").empty)
                                    newInlines ~= evals.front.makeInlines(DocFormat.markdown);
                                else
                                    newInlines ~= Inline.as!"Str"(evals.front);

                                evals.popFront();
                            }else
                                newInlines ~= a;
                        }

                        arr = newInlines;
                    },
                    idxset.length
                );

                return false;
            }


            Block[] fB(ref Block[] arr)
            {
                bool[size_t] idxset;

                Block[] rets;
                foreach(idx, ref a; arr){
                    if(auto p = a.content.peek!(Block.CodeBlock)){
                        if(!p.attr.classes.find("dcode").empty){
                            if(!p.attr.classes.find("emplace").empty){
                                dcode ~= `_evaled__ ~= to!string(`;
                                dcode ~= p.content;
                                dcode ~= `);`"\n";
                                idxset[rets.length] = true;
                                rets ~= a;
                            }else
                                dcode ~= p.content;
                        }else
                            rets ~= a;
                    }else
                        rets ~= a;
                }


                if(idxset.length == 0)
                    return rets;

                evals ~= Evaled(
                    (string[] evals){
                        Block[] newBlocks;

                        foreach(i, ref a; rets){
                            if(i in idxset){
                                auto p = a.content.peek!(Block.CodeBlock)
                                         .enforce("Unknown error");

                                if(!p.attr.classes.find("emplace").empty){
                                    newBlocks ~= evals.front.makeBlocks(DocFormat.markdown);
                                    evals.popFront();
                                }
                            }else
                                newBlocks ~= a;
                        }
                        arr = newBlocks;
                    },
                    idxset.length
                );

                arr = rets;
                return false;
            }

            static if(is(typeof(a) == Inline[]))
                return fI(a);
            else static if(is(typeof(a) == Block[]))
                return fB(a);
            else
                return false;
        })();

    dcode ~= `
        import std.algorithm, std.array;
        meta["_eval_cache_"] = JSONValue(array(map!(a => JSONValue(a))(_evaled__)));
        //writeln(_evaled__);
    }`;


    immutable dcodeFile = "docgen_codegen.d";
    immutable savedJSON = "docgen_jsongen.json";

    void evalDCode()
    {
        import std.datetime : Clock;

        std.file.write(dcodeFile, dcode.data);

        auto rdmd = execute(["rdmd", dcodeFile]);
        enforce(rdmd.status == 0, rdmd.output);
    }


    if(!exists(dcodeFile) || readText(dcodeFile) != dcode.data)
        evalDCode();
    else
    {
        enforce(exists(savedJSON));

        auto metaTempSave = readText(savedJSON).parseJSON();
        auto metaString = readText("meta.json");
        if(metaTempSave["_meta_cache_"].str != metaString)
            evalDCode();
    }

    auto metaTempSave = readText(savedJSON).parseJSON();
    string[] evaledTable = metaTempSave["_eval_cache_"].array.map!"a.str".array;

    foreach(i, ref ev; evals){
        ev.dg(evaledTable[0 .. ev.cnt]);
        evaledTable = evaledTable[ev.cnt .. $];
    }

    {
        Meta.Value[string] unMeta;
        foreach(k, ref v; metaTempSave.object){
            //if(k == "_eval_cache_" || k == "_meta_cache_") continue;
            if(k[0] == '_' && k[$-1] == '_') continue;

            unMeta[k] = makeMetaValue(v);
        }

        doc.meta = Meta(unMeta);
    }
}


void replaceIncludeBlock(ref Pandoc doc)
{
    doc.walk!((ref Block[] blocks){
        Block[] res;

        foreach(ref blk; blocks){
            if(auto p = blk.content.peek!(Block.CodeBlock)){
                if(!p.attr.classes.find("includeBlocks").empty){
                    foreach(fname; p.content.splitLines){
                        string txt = readText(fname);
                        auto pd = txt.convertByPandoc(DocFormat.markdown, DocFormat.json).parseJSON().fromJSONValue!Pandoc;
                        pd.replaceIncludeBlock();
                        res ~= pd.getTopBlocks();
                    }
                    continue;
                }
            }

            res ~= blk;
        }

        blocks = res;
        return false;
    });
}


void sortBibitems(ref Pandoc doc, string bibTag = "bibitems")
{
    string[] bibList;

    doc.walk!((ref Inline.RawInline elem){
        if(elem.format == "tex" && elem.content.startsWith("\\cite{"))
            bibList ~= elem.content[6 .. $-1].split(",").map!(strip).array();

        return true;
    });


    Meta.Value[string] bibHash;
    auto mm = (bibTag in doc.meta.unMeta).enforce("meta does not have a tag`" ~ bibTag ~ "` as bibliographies.")
              .content.peek!(Meta.Value.MetaList).enforce();

    foreach(ref v; mm.content){
        auto p = v.content.peek!(Meta.Value.MetaMap).enforce("bibitem is not a instance of MetaMap.");
        auto tag = enforce("tag" in p.content, "bibitem does not have the field of `item`.")
                   .content.peek!(Meta.Value.MetaInlines).enforce("bibitem's \"tag\" does not have inlines.")
                   .content.convertByPandoc(DocFormat.markdown);

        enforce("info" in p.content, "bibitem does not have the field of `info`.")
        .content.peek!(Meta.Value.MetaInlines).enforce("bibitem's `info` does not have inlines.")
        .content
        .walk!((ref Inline s){
            if(auto p = s.peek!(Inline.Str)){
                if(p.content.startsWith("pp.")){
                    s = Inline.as!"RawInline"(Format("tex"), p.content);
                    return true;
                }
                else
                    return false;
            }else return false;
        });

        bibHash[tag] = v;
    }

    bool[string] oldSet;
    Meta.Value[] newList;
    size_t[string] tagIdx;
    foreach(tag; bibList){
        if(auto p = tag in bibHash){
            tagIdx[tag] = newList.length;
            newList ~= *p;
            bibHash.remove(tag);
            oldSet[tag] = true;
        }else
            enforce(tag in oldSet, format("tag:`%s` is not defined.", tag));
    }

    // set meta
    *mm = Meta.Value.MetaList(newList);

    // sort multi-cites
    doc.walk!((ref Inline.RawInline elem){
        if(elem.format == "tex" && elem.content.startsWith("\\cite{")){
            auto arr = elem.content[6 .. $-1].split(",").map!strip.map!(a => tuple(a, tagIdx[a])).array();
            arr.sort!"a[1] < b[1]"();
            elem.content = format("\\cite{%-(%s,%)}", arr.map!"a[0]");
        }

        return true;
    });
}
pandoc.d
//module hyde.pandoc;

import std.algorithm;
import std.array;
import std.conv;
import std.exception;
import std.json;
import std.string;
import std.traits;
import std.typecons;
import std.typetuple;
import std.variant;
import std.process;
import std.meta;
import std.file;


import std.stdio;


enum DocFormat : string
{
    json = "json",
    markdown = "markdown",
    html = "html",
    html5 = "html5",
    latex = "latex"
}


string convertByPandoc(string doc, DocFormat from, DocFormat to, string[] opt...)
{
    auto pandoc = pipeProcess(["pandoc", "--from=" ~ from, "--to=" ~ to] ~ opt, Redirect.stdin | Redirect.stdout);
    scope(exit) wait(pandoc.pid);

    auto sendData = cast(immutable(ubyte)[])(doc);
    pandoc.stdin.rawWrite(sendData);
    pandoc.stdin.close();

    auto str = appender!string();
    foreach(ubyte[] buf; pandoc.stdout.byChunk(4096))
        str ~= cast(string)(buf.dup);

    return str.data;
}


Pandoc makePandoc(string doc, DocFormat from)
{
    auto json = doc.convertByPandoc(DocFormat.markdown, DocFormat.json);
    auto jsonValue = json.parseJSON();

    return jsonValue.fromJSONValue!Pandoc();
}


Block[] getTopBlocks(Pandoc doc)
{
    return doc.document;
}


Block[] makeBlocks(string doc, DocFormat from = DocFormat.markdown)
{
    return doc.makePandoc(from).getTopBlocks;
}


string convertByPandoc(Block[] blocks, DocFormat to)
{
    Pandoc doc;
    doc.document = blocks;
    return doc.toJSONValue.toJSONFormatString().convertByPandoc(DocFormat.json, to);
}


Inline[] makeInlines(string doc, DocFormat from = DocFormat.markdown)
{
    auto blks = doc.makeBlocks(from);
    enforce(blks.length == 1, blks.toJSONValue.toJSONFormatString());

    auto para = blks[0].peek!(Block.Para);
    enforce(para);

    return para.content;
}


string convertByPandoc(Inline[] inlines, DocFormat to)
{
    Block[] blks = [Block.as!"Para"(inlines)];
    return blks.convertByPandoc(to).strip();
}


Meta.Value makeMetaValueFromJSON(string json)
{
    return json.parseJSON().makeMetaValue();
}


Meta.Value makeMetaValue(JSONValue json)
{
    final switch(json.type)
    {
      case JSON_TYPE.NULL:
        enforce(0);
        break;

      case JSON_TYPE.STRING:
        auto blks = makeBlocks(json.str);
        if(blks.length == 1 && blks[0].peek!(Block.Para) != null)
            return Meta.Value.as!"MetaInlines"(blks[0].peek!(Block.Para).content);
        else
            return Meta.Value.as!"MetaBlocks"(blks);

      case JSON_TYPE.INTEGER:
        return Meta.Value.as!"MetaInlines"([Inline.as!"Str"(json.integer.to!string)]);

      case JSON_TYPE.UINTEGER:
        return Meta.Value.as!"MetaInlines"([Inline.as!"Str"(json.uinteger.to!string)]);

      case JSON_TYPE.FLOAT:
        return Meta.Value.as!"MetaInlines"([Inline.as!"Str"(json.floating.to!string)]);

      case JSON_TYPE.OBJECT:
        Meta.Value.MetaMap metaMap;
        foreach(k, ref v; json.object)
            metaMap.content[k] = makeMetaValue(v);
        return Meta.Value(metaMap);

      case JSON_TYPE.ARRAY:
        Meta.Value.MetaList metaList;
        foreach(ref v; json.array)
            metaList.content ~= makeMetaValue(v);
        return Meta.Value(metaList);

      case JSON_TYPE.TRUE:
        return Meta.Value.as!"MetaBool"(true);

      case JSON_TYPE.FALSE:
        return Meta.Value.as!"MetaBool"(false);
    }

    assert(0);
}


private{
    /**
    UDAのための型です。
    この型がAttributeとして付いている変数には、walkが再帰的に適用されます
    */
    struct ApplyWalk{}

    /**
    UDAのための型です。
    この型がAttributeとして付いている型は、Pandocが吐くJSON formatted ASTではOBJECTとして扱われます。
    */
    struct AsObjectInJSON
    {
        /**
        UDAのための型です。
        OBJECTでの名前を指定するために使用します。
        */
        struct Name
        {
            string name;        /// OBJECTでの名前
        }
    }


    /**
    構造体などのメンバに対して、再帰的にwalkを働かせるかどうか判定します。
    */
    template isAppliedWalkField(alias field)
    {
        enum isAppliedWalkField = staticIndexOf!(ApplyWalk, __traits(getAttributes, field)) != -1;
    }


    /**
    Pandocが吐くJSON formatted ASTにおいてOBJECTとして表現されているか判断します。
    */
    template isObjectInJSON(alias field)
    {
        enum isObjectInJSON = staticIndexOf!(AsObjectInJSON, __traits(getAttributes, field)) != -1;
    }


    /**
    Pandocが吐くJSON formatted ASTにおいてOBJECTとして表現されている場合、対応する名前を返します。
    */
    template getNameInJSONObject(alias field)
    {
        template isAsObjectInJSONName(alias A)
        {
            static if(isExpressionTuple!A)
                enum isAsObjectInJSONName = is(typeof(A) == AsObjectInJSON.Name);
            else
                enum isAsObjectInJSONName = false;
        }

        enum idx = staticIndexOf!(isAsObjectInJSONName, __traits(getAttributes, field));

        static if(idx != -1)
            enum getNameInJSONObject = __traits(getAttributes, field)[idx].name;
        else
            enum getNameInJSONObject = field.stringof;
    }


    /**
    Algebraicを使いやすくするためのテンプレート
    */
    mixin template UseAlgebraic(T...)
    {
        Algebraic!(T) content;

        static auto as(string type, T...)(T args)
        {
            mixin("alias U = " ~ type ~ ";");
            return typeof(this)(U(args));
        }


        ref typeof(this) opAssign(T)(auto ref T content)
        if(staticIndexOf!(T, InnerTypes) != -1)
        {
            content = t;
            return this;
        }
    }
}


/**
Pandocが吐くAST
*/
struct Pandoc
{
    @ApplyWalk Meta meta;           // メタ情報を管理する
    @ApplyWalk Block[] document;    // ドキュメントのAST
}


/**
ドキュメントのメタ情報を管理します
*/
@AsObjectInJSON
struct Meta
{
    /**
    キーに"title"や"author", "date"が格納され、値にはそれに対応したものが格納されています。

    Example:
    --------
    ["title": Meta.Value(Meta.Value.MetaInlines([Inline(Inline.Str("d-manual"))])),
     "author":Meta.Value(Meta.Value.MetaList([Meta.Value(Meta.Value.MetaInlines([Inline(Inline.Str("Kazuki")), Inline(Inline.Space()), Inline(Inline.Str("Komatsu"))]))])),
     "date":  Meta.Value(Meta.Value.MetaInlines([Inline(Inline.Str("May")), Inline(Inline.Space()), Inline(Inline.Str("5,")), Inline(Inline.Space()), Inline(Inline.Str("2013")), Inline(Inline.Space()), Inline(Inline.Str("~"))]))]
    --------
    */
    @(AsObjectInJSON.Name("unMeta")) @ApplyWalk
    Value[string] unMeta;
    alias unMeta this;


    struct Value
    {
        this(T)(T t)
        if(staticIndexOf!(T, InnerTypes) != -1)
        {
            content = t;
        }


        alias InnerTypes = TypeTuple!(MetaMap, MetaList, MetaBool, MetaString, MetaInlines, MetaBlocks);

        struct MetaMap { @ApplyWalk Value[string] content; }
        struct MetaList { @ApplyWalk Value[] content; }
        struct MetaBool { @ApplyWalk bool content; }
        struct MetaString { @ApplyWalk string content; }
        struct MetaInlines { @ApplyWalk Inline[] content; }
        struct MetaBlocks { @ApplyWalk Block[] content; }

        mixin UseAlgebraic!(InnerTypes);
        alias content this;
    }
}


enum Alignment : string
{
    left = "AlignLeft",
    right = "AlignRight",
    center = "AlignCenter",
    default_ = "AlignDefault",
}


struct ListAttributes
{
    enum NumberStyle : string
    {
        default_ = "DefaultStyle",
        example = "Example",
        decimal = "Decimal",
        lowerRoman = "LowerRoman",
        upperRoman = "UpperRoman",
        lowerAlpha = "LowerAlpha",
        upperAlpha = "UpperAlpha",
    }


    enum NumberDelim : string
    {
        default_ = "DefaultDelim",
        period = "Period",
        oneParen = "OneParen",
        twoParen = "TwoParen",
    }


    int level;
    NumberStyle numStyle;
    NumberDelim numDelim;
}


struct Attr
{
    string identifier;
    string[] classes;
    Tuple!(string, string)[] keyval;
}


alias TableCell = Block[];


struct Format { string content; alias content this; }


struct Block
{
    this(T)(T t)
    if(staticIndexOf!(T, InnerTypes) != -1)
    {
        content.opAssign(t);
    }


    alias InnerTypes = TypeTuple!(Plain, Para, CodeBlock, RawBlock, BlockQuote, OrderedList,
                                  BulletList, DefinitionList, Header, HorizontalRule, Table,
                                  Div, Null);

    struct Plain { @ApplyWalk Inline[] content; }
    struct Para { @ApplyWalk Inline[] content; }
    struct CodeBlock { Attr attr; @ApplyWalk string content; }
    struct RawBlock { Format format; @ApplyWalk string content; }
    struct BlockQuote { @ApplyWalk Block[] content; }
    struct OrderedList { ListAttributes attr; @ApplyWalk Block[][] content; }
    struct BulletList { @ApplyWalk Block[][] content; }
    struct DefinitionList { @ApplyWalk Element[] content; struct Element { @ApplyWalk Inline[] term; @ApplyWalk Block[][] defs; } }
    struct Header { int level; Attr attr; @ApplyWalk Inline[] content; }
    struct HorizontalRule{}
    struct Table { @ApplyWalk Inline[] caption; Alignment[] colAligns; double[] relColWidth; @ApplyWalk TableCell[] colHeaders; @ApplyWalk TableCell[][] rows; }
    struct Div { Attr attr; @ApplyWalk Block[] content; }
    struct Null {}


    mixin UseAlgebraic!(InnerTypes);
    alias content this;
}


struct Target { string url; string title; }


enum MathType : string { display = "DisplayMath", inline = "InlineMath" }


struct Inline
{
    this(T)(T t)
    if(staticIndexOf!(T, InnerTypes) != -1)
    {
        content = t;
    }


    alias InnerTypes = TypeTuple!(Str, Emph, Strong, Strikeout, Superscript, Subscript, SmallCaps,
                                  Quoted, Cite, Code, Space, LineBreak, Math, RawInline, Link,
                                  Image, Note, Span);

    struct Str { @ApplyWalk string content; alias content this; }
    struct Emph { @ApplyWalk Inline[] content; alias content this; }
    struct Strong { @ApplyWalk Inline[] content; alias content this; }
    struct Strikeout { @ApplyWalk Inline[] content; alias content this; }
    struct Superscript { @ApplyWalk Inline[] content; alias content this; }
    struct Subscript { @ApplyWalk Inline[] content; alias content this; }
    struct SmallCaps { @ApplyWalk Inline[] content; alias content this; }
    struct Quoted { Type type; @ApplyWalk Inline[] content; enum Type : string { singleQuote = "SingleQuote", doubleQuote = "DoubleQuote" } }
    struct Cite { Citation[] citation; @ApplyWalk Inline[] content; alias content this;  }
    struct Code { Attr attr; @ApplyWalk string content; alias content this; }
    struct Space {}
    struct LineBreak {}
    struct Math { MathType type; @ApplyWalk string content; alias content this; }
    struct RawInline { Format format; @ApplyWalk string content; alias content this; }
    struct Link { @ApplyWalk Inline[] content; alias content this; Target target; }
    struct Image { @ApplyWalk Inline[] content; alias content this; Target target; }
    struct Note { @ApplyWalk Block[] content; alias content this; }
    struct Span { Attr attr; @ApplyWalk Inline[] content; alias content this; }


    mixin UseAlgebraic!(InnerTypes);
    alias content this;
}


@AsObjectInJSON struct Citation
{
    enum Mode : string
    {
        authorInText = "AuthorInText",
        suppressAuthor = "SuppressAuthor",
        normalCitation = "NormalCitation",
    }


    @(AsObjectInJSON.Name("citationId")) string id;
    @(AsObjectInJSON.Name("citationPrefix")) Inline[] prefix;
    @(AsObjectInJSON.Name("citationSuffix")) Inline[] suffix;
    @(AsObjectInJSON.Name("citationMode")) Mode mode;
    @(AsObjectInJSON.Name("citationNoteNum")) int noteNum;
    @(AsObjectInJSON.Name("citationHash")) int hash;
}


JSONValue toJSONValue(T)(T value)
if(is(T == typeof(null)))
{
    JSONValue dst = null;

    return dst;
}


T fromJSONValue(T)(JSONValue json)
if(is(T == string) && !is(T == enum))
in{
    assert(json.type == JSON_TYPE.STRING);
}
body{
    return json.str;
}


JSONValue toJSONValue(T)(T value)
if(is(T == string))
out(result){
    assert(result.type == JSON_TYPE.STRING);
}
body{
    JSONValue dst = value;

    return dst;
}


T fromJSONValue(T : int)(JSONValue json)
in{
    assert(json.type == JSON_TYPE.INTEGER);
}
body{
    return json.integer.to!int();
}


JSONValue toJSONValue(T)(T value)
if(isSigned!T && isIntegral!T)
out(result){
    assert(result.type == JSON_TYPE.INTEGER);
}
body{
    JSONValue dst = value;

    return dst;
}


JSONValue toJSONValue(T)(T value)
if(isUnsigned!T && isIntegral!T)
out(result){
    assert(result.type == JSON_TYPE.UINTEGER);
}
body{
    JSONValue dst = value;

    return dst;
}


T fromJSONValue(T : bool)(JSONValue json)
in{
    assert(json.type == JSON_TYPE.TRUE || json.type == JSON_TYPE.FALSE);
}
body{
    return json.type == JSON_TYPE.TRUE;
}


JSONValue toJSONValue(T)(T value)
if(is(T == bool))
out(result){
    assert(result.type == JSON_TYPE.TRUE || result.type == JSON_TYPE.FALSE);
}
body{
    JSONValue dst = value;

    return dst;
}


T fromJSONValue(T : double)(JSONValue json)
in{
    assert(json.type == JSON_TYPE.FLOAT);
}
body{
    return json.floating;
}


JSONValue toJSONValue(T)(T value)
if(isFloatingPoint!T)
out(result){
    assert(result.type == JSON_TYPE.FLOAT);
}
body{
    JSONValue dst = value;

    return dst;
}


T fromJSONValue(T)(JSONValue json)
if(is(T == E[], E) && !is(E : char) && !is(T == struct))
in{
    assert(json.type == JSON_TYPE.ARRAY);
}
body{
    return json.array.map!(a => a.fromJSONValue!(typeof(T.init[0]))()).array();
}


JSONValue toJSONValue(T)(T value)
if(!is(T == enum) && !is(T == struct) && is(T : E[], E) && !is(E : char))
out(result){
    assert(result.type == JSON_TYPE.ARRAY);
}
body{
    JSONValue dst;
    dst.array = null;

    foreach(e; value)
        dst.array ~= e.toJSONValue();

    return dst;
}


T fromJSONValue(T)(JSONValue json)
if(is(T == V[K], V, K) && !is(T == struct))
in{
    assert(json.type == JSON_TYPE.OBJECT);
}
body{
    alias V = typeof(T.init.values[0]);
    T dst;

    foreach(k, v; json.object)
        dst[k] = v.fromJSONValue!V();

    return dst;
}


JSONValue toJSONValue(T)(T value)
if(is(T == V[K], V, K) && !is(T == struct))
out(result){
    assert(result.type == JSON_TYPE.OBJECT);
}
body{
    JSONValue dst;
    dst.object = null;

    foreach(k, v; value)
        dst.object[k] = v.toJSONValue();

    return dst;
}


T fromJSONValue(T)(JSONValue json)
if((is(T == enum) && is(T : string)) || is(typeof(T.InnerTypes.length)) || (is(T == struct) && isObjectInJSON!T) || is(T == struct))
{
    static if(is(T == enum) && is(T : string))
    {
        assert(json.type == JSON_TYPE.OBJECT);

        foreach(e; EnumMembers!T)
            if(json["t"].fromJSONValue!string() == e)
                return e;

        enforce(0, format(`JSON parser of pandoc AST throw an error "%s".`, format("Invalid type tag '%s' of " ~ T.stringof, json["t"].fromJSONValue!string())));
        assert(0);
    }
    else static if(is(typeof(T.InnerTypes.length)))
    {
        assert(json.type == JSON_TYPE.OBJECT);

        foreach(i, U; T.InnerTypes)
            if(json["t"].fromJSONValue!string() == U.stringof)
                return T(json["c"].fromJSONValue!(T.InnerTypes[i])());

        enforce(0, format(`JSON parser of pandoc AST throw an error "%s".`, format("Invalid type tag '%s' of " ~ T.stringof, json["t"].fromJSONValue!string())));
        assert(0);
    }
    else static if(is(T == struct) && isObjectInJSON!T)
    {
        assert(json.type == JSON_TYPE.OBJECT);

        T dst;

        foreach(i, ref e; dst.tupleof){
            if(auto p = getNameInJSONObject!(T.tupleof[i]) in json.object)
                e = fromJSONValue!(typeof(e))(*p);
            else
                enforce(0, format(`JSON parser of pandoc AST throw an error "%s".`, format("Cannot find object member name '%s' of " ~ T.stringof, getNameInJSONObject!(T.tupleof[i]))));
        }

        return dst;
    }
    else static if(is(T == struct))
    {
        T dst;

        static if(T.tupleof.length == 1)
            dst = T(json.fromJSONValue!(typeof(T.tupleof))());
        else
        {
            enforce(json.type == JSON_TYPE.ARRAY, format(`JSON parser of pandoc AST throw an error "%s".`, format("JSON_TYPE is '%s', not '%s' in '%s'", json.type, JSON_TYPE.ARRAY, __FUNCTION__)));

            foreach(i, ref e; dst.tupleof)
                e = json[i].fromJSONValue!(typeof(T.tupleof[i]))();
        }

        return dst;
    }
    else
        static assert(0, "'" ~ T.stringof ~ "' is not matched.");
}


JSONValue toJSONValue(T)(T value)
if(is(T == enum) && is(T : string))
{
    JSONValue dst;
    dst.object = null;

    dst.object["t"] = (cast(string)value).toJSONValue();
    dst.object["c"] = cast(JSONValue[])[];

    return dst;
}


JSONValue toJSONValue(T)(T value)
if(is(typeof(T.InnerTypes.length)))
{
    JSONValue dst;
    dst.object = null;

    foreach(i, U; T.InnerTypes)
        if(auto p = value.tupleof[0].peek!U())
        {
            dst.object["t"] = U.stringof.toJSONValue();
            dst.object["c"] = (*p).toJSONValue();
        }

    return dst;
}


JSONValue toJSONValue(T)(T value)
if(is(T == struct) && isObjectInJSON!T)
{
    JSONValue dst;
    dst.object = null;

    foreach(i, e; value.tupleof)
        dst.object[getNameInJSONObject!(T.tupleof[i])] = e.toJSONValue();

    return dst;
}


JSONValue toJSONValue(T)(T value)
if(is(T == struct) && !isObjectInJSON!T && !is(typeof(T.InnerTypes.length)))
{
    JSONValue dst;

    static if(T.tupleof.length == 0)
    {
        dst = cast(JSONValue[])null;
    }
    else static if(T.tupleof.length == 1)
        dst = value.tupleof[0].toJSONValue();
    else
    {
        dst.array = null;
        foreach(i, ref e; value.tupleof)
            dst.array ~= e.toJSONValue();
    }

    return dst;
}


string toJSONFormattedString(JSONValue json)
{
    return toJSON(&json);
}


unittest
{
    import std.stdio;


    string jsonASTMeta = `{"unMeta":{}}`;
    assert(jsonASTMeta.parseJSON().fromJSONValue!Meta() == Meta(null));
    assert(Meta(null).toJSONValue().toJSONFormattedString() == jsonASTMeta);


    jsonASTMeta = `{"unMeta":{"title":{"c":[{"c":"d-manual","t":"Str"}],"t":"MetaInlines"}}}`;
    assert(jsonASTMeta.parseJSON().fromJSONValue!(Meta)() == Meta(["title":Meta.Value.as!"MetaInlines"([Inline.as!"Str"("d-manual")])]));
    assert(jsonASTMeta.parseJSON().fromJSONValue!(Meta)().toJSONValue().toJSONFormattedString() == jsonASTMeta);


    jsonASTMeta =
    `{"unMeta":{"title":{"t":"MetaInlines","c":[{"t":"Str","c":"d-manual"}]},
                "author":{"t":"MetaList","c":[{"t":"MetaInlines","c":[{"t":"Str","c":"Kazuki"},{"t":"Space","c":[]},{"t":"Str","c":"Komatsu"}]}]},
                "date":{"t":"MetaInlines","c":[{"t":"Str","c":"May"},{"t":"Space","c":[]},{"t":"Str","c":"5,"},{"t":"Space","c":[]},{"t":"Str","c":"2013"},{"t":"Space","c":[]},{"t":"Str","c":"~"}]}}}`;
    
    assert(jsonASTMeta.parseJSON().fromJSONValue!Meta()
        == Meta(["title": Meta.Value.as!"MetaInlines"([Inline(Inline.Str("d-manual"))]),
                 "author":Meta.Value.as!"MetaList"([Meta.Value.as!"MetaInlines"([Inline.as!"Str"("Kazuki"), Inline.as!"Space", Inline.as!"Str"("Komatsu")])]),
                 "date":  Meta.Value.as!"MetaInlines"([Inline.as!"Str"("May"), Inline.as!"Space", Inline.as!"Str"("5,"), Inline.as!"Space", Inline.as!"Str"("2013"), Inline.as!"Space", Inline.as!"Str"("~")])])
    );
    assert(jsonASTMeta.parseJSON().fromJSONValue!Meta().toJSONValue().toJSONFormattedString().parseJSON().fromJSONValue!Meta() == jsonASTMeta.parseJSON().fromJSONValue!Meta());
}


void walk(alias f, T)(ref T tree)
{
  static if(is(typeof(ParameterTypeTuple!f.length)) && is(ParameterTypeTuple!f[0] == T))
    if(f(tree)) return;
  else static if(is(typeof(f(tree))))
    if(f(tree)) return;

  static if(isArray!T)
  {
    foreach(ref e; tree)
        walk!f(e);
  }
  else static if(is(typeof(T.InnerTypes.length)))
  {
    foreach(U; T.InnerTypes)
        if(U* p = tree.content.peek!U)
              walk!f(*p);
  }
  else static if(is(typeof(tree.tupleof.length)))
  {
    foreach(i, ref e; tree.tupleof)
        static if(isAppliedWalkField!(T.tupleof[i]))
            walk!f(e);
  }
}


unittest{
    import std.stdio;


    string jsonASTMeta = `{"unMeta":{}}`;
    auto walked = jsonASTMeta.parseJSON().fromJSONValue!Meta();
    walked.walk!((ref Meta.Value[string] unMeta) { unMeta = ["title": Meta.Value.as!"MetaInlines"([Inline.as!"Str"("d-manual")]),
                 "author":Meta.Value.as!"MetaList"([Meta.Value.as!"MetaInlines"([Inline.as!"Str"("Kazuki"), Inline.as!"Space", Inline.as!"Str"("Komatsu")])]),
                 "date":  Meta.Value.as!"MetaInlines"([Inline.as!"Str"("May"), Inline.as!"Space", Inline.as!"Str"("5,"), Inline.as!"Space", Inline.as!"Str"("2013"), Inline.as!"Space", Inline.as!"Str"("~")])];
                 return true;
    })();

    jsonASTMeta = `{"unMeta":{"title":{"t":"MetaInlines","c":[{"t":"Str","c":"d-manual"}]},
                "author":{"t":"MetaList","c":[{"t":"MetaInlines","c":[{"t":"Str","c":"Kazuki"},{"t":"Space","c":[]},{"t":"Str","c":"Komatsu"}]}]},
                "date":{"t":"MetaInlines","c":[{"t":"Str","c":"May"},{"t":"Space","c":[]},{"t":"Str","c":"5,"},{"t":"Space","c":[]},{"t":"Str","c":"2013"},{"t":"Space","c":[]},{"t":"Str","c":"~"}]}}}`;
    assert(walked == jsonASTMeta.parseJSON().fromJSONValue!Meta());

    auto jsonASTPandoc = `[{"unMeta":{"title":{"t":"MetaInlines","c":[{"t":"Str","c":"d-manual"}]},"author":{"t":"MetaList","c":[{"t":"MetaInlines","c":[{"t":"Str","c":"Kazuki"},{"t":"Space","c":[]},{"t":"Str","c":"Komatsu"}]}]},"date":{"t":"MetaInlines","c":[{"t":"Str","c":"May"},{"t":"Space","c":[]},{"t":"Str","c":"5,"},{"t":"Space","c":[]},{"t":"Str","c":"2013"},{"t":"Space","c":[]},{"t":"Str","c":"~"}]}}},[{"t":"Header","c":[1,["this-is-header1",[],[]],[{"t":"Str","c":"barrrr"}]]},{"t":"Para","c":[{"t":"Str","c":"hoge"},{"t":"Space","c":[]},{"t":"Str","c":"(in"},{"t":"Space","c":[]},{"t":"Str","c":"Header(1))"}]},{"t":"Header","c":[2,["foo",[],[]],[{"t":"Str","c":"barrrr"}]]},{"t":"Para","c":[{"t":"Str","c":"foo"},{"t":"Space","c":[]},{"t":"Str","c":"bar(in"},{"t":"Space","c":[]},{"t":"Str","c":"Header(2))"}]}]]`;

    auto walked2 = jsonASTPandoc.parseJSON().fromJSONValue!Pandoc();
    walked2.walk!((ref Block.Header h){ h.content = [Inline.as!"Str"("barrrr")];  return true; });
    assert(walked2 == jsonASTPandoc.parseJSON().fromJSONValue!Pandoc());
}


string toJSONFormatString(JSONValue json)
{
    return toJSON(&json);
}

d CEF4D示例(来自http://dpaste.dzfl.pl/353cbe454324)

CEF4D示例(来自http://dpaste.dzfl.pl/353cbe454324)

gistfile1.d
import core.thread;
import core.time;

import std.stdio;

import cef4d.c.capi.cef_base_capi;
import cef4d.c.capi.cef_app_capi;
import cef4d.c.capi.cef_client_capi;

import cef4d.base;
import cef4d.app;
import cef4d.client;

/*
一応、DのクラスをCのAPIに渡せるようになった。
cef_app_t
*/
class CEFApp : ICEFApp
{
    this() {}

    void onBeforeCommandLineProcessing(cef_string_t*, cef_command_line_t*) { writeln("Called: ", __FUNCTION__); }
    void onRegisterCustomSchemes(cef_scheme_registrar_t*) { writeln("Called: ", __FUNCTION__); }
    cef_resource_bundle_handler_t* getResourceBundleHandler() { writeln("Called: ", __FUNCTION__); return null; }
    cef_browser_process_handler_t* getBrowserProcessHandler() { writeln("Called: ", __FUNCTION__); return null; }
    cef_render_process_handler_t* getRenderProcessHandler() { writeln("Called: ", __FUNCTION__); return null; }
}

/*
一応、DのクラスをCのAPIに渡せるようになった。
cef_client_t
*/
class CEFClient : ICEFClient
{
    this() {}

    cef_context_menu_handler_t* getContextMenuHandler() { writeln(__FUNCTION__); return null; }
    cef_dialog_handler_t* getDialogHandler() { writeln(__FUNCTION__); return null; }
    cef_display_handler_t* getDisplayHandler() { writeln(__FUNCTION__); return null; }
    cef_download_handler_t* getDownloadHandler() { writeln(__FUNCTION__); return null; }
    cef_drag_handler_t* getDragHandler() { writeln(__FUNCTION__); return null; }
    cef_focus_handler_t* getFocusHandler() { writeln(__FUNCTION__); return null; }
    cef_geolocation_handler_t* getGeolocationHandler() { writeln(__FUNCTION__); return null; }
    cef_jsdialog_handler_t* getJSDialogHandler() { writeln(__FUNCTION__); return null; }
    cef_keyboard_handler_t* getKeyboardHandler() { writeln(__FUNCTION__); return null; }
    cef_life_span_handler_t* getLifeSpanHandler() { writeln(__FUNCTION__); return null; }
    cef_load_handler_t* getLoadHandler() { writeln(__FUNCTION__); return null; }
    cef_render_handler_t* getRenderHandler() { writeln(__FUNCTION__); return null; }
    cef_request_handler_t* getRequestHandler() { writeln(__FUNCTION__); return null; }
    int onProcessMessageReceived(cef_browser_t* browser,
        cef_process_id_t sourceProcess, cef_process_message_t* message)
    {
        writeln(__FUNCTION__);
        return 0;
    }
}

//__gshared bool IsRunning = true;


void main( string[] args ) {
    //DerelictCEF.load();

    cef_main_args_t mainArgs;
    version( Windows ) {
        import core.sys.windows.windows : GetModuleHandleA;
        mainArgs.instance = GetModuleHandleA( null );
    } else {
        char** cargs = new char**[]( args.length );
        foreach( i,s; args )
        {
            cargs[ i ] = s.toStringz();
        }
        mainArgs.argc = cast( int )args.length;
        mainArgs.argv = cargs;
    }

    auto app = cefScopeGuard!CEFApp();

    writeln( "Executing process..." );
    int code = cef_execute_process( &mainArgs, app.cptr, null );
    if( code >= 0 )
    {
        writeln( "Exiting with code ", code );
        throw new Error( "Bye bye!" );
    }

    cef_settings_t settings;
    settings.size = cef_settings_t.sizeof;
    settings.no_sandbox = 1;

    writeln( "Initializing cef..." );
    cef_initialize( &mainArgs, &settings, app.cptr, null );

    cef_string_t windowName;
    cef_string_utf8_to_utf16( "Chromium Embedded Framework for D".ptr, "Chromium Embedded Framework for D".length, &windowName );

    cef_window_info_t windowInfo;
    version( Windows )
    {
        import core.sys.windows.windows;
        windowInfo.window_name = windowName;
        windowInfo.style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE;
        windowInfo.parent_window = null;
        windowInfo.x = CW_USEDEFAULT;
        windowInfo.y = CW_USEDEFAULT;
        windowInfo.width = CW_USEDEFAULT;
        windowInfo.height = CW_USEDEFAULT;
    }
    else
    {
        static assert( 0, "Not yet implemented." );
    }

    auto initURL = "http://dlang.org";
    cef_string_t url;
    cef_string_utf8_to_utf16( initURL.ptr, initURL.length, &url );

    cef_browser_settings_t browserSettings;
    browserSettings.size = cef_browser_settings_t.sizeof;

    auto client = cefScopeGuard!CEFClient();

    writeln( "Creating the browser..." );
    cef_browser_host_create_browser( &windowInfo, client.cptr, &url, &browserSettings, null );

    writeln( "Running the message loop..." );
    cef_run_message_loop();

    writeln( "Shutting down..." );
    cef_shutdown();
}

d Dtrace片段用于观察ZIL的行为

Dtrace片段用于观察ZIL的行为

zil-log-io-size-buckets.d
#!/usr/sbin/dtrace -Cs
#include <sys/fs/zfs.h>
#pragma D option quiet
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright (c) 2016, RackTop Systems.
 */

 string logbias[zfs_logbias_op_t];
 int x;
 inline int BLKSZ_ABOVE_128K = -0xffff; /* Just a magic value */

/* This is a good way to play with this a little bit. Just copy and paste in
 * a separate shell and let it roll. Make sure /bp/test exists.
  while :; do dd if=/dev/urandom of=/bp/test/out.bin bs=4096 count=$RANDOM ; 
  dd if=/dev/urandom of=/bp/test/out.bin bs=0x2000 count=$RANDOM ; 
  dd if=/dev/urandom of=/bp/test/out.bin bs=0x4000 count=$RANDOM ; 
  sleep 1; done
*/

BEGIN {
  printf("%s,%s,%s,%s,%s,%s,%s,%s,%s\n", "poolname", "bias",
    "4K", "8K", "16K", "32K", "64K", "128K", "gt.128K"
  );
  logbias[ZFS_LOGBIAS_LATENCY] = "latency";
  logbias[ZFS_LOGBIAS_THROUGHPUT] = "throughput";
}

::zil_lwb_write_start:entry {
  this->pooln = stringof(args[0]->zl_dmu_pool->dp_spa->spa_name);
  this->size = args[1]->lwb_sz;
  this->lb = args[0]->zl_logbias;
  /* We need to fit into one of these buckewith anything larger than
   * 128K tagged with BLKSZ_ABOVE_128K, and ultimately resulting in
   * being counted by @bgt128k aggregate.
   */
  this->buck = 0x1000; /* We assume this is smallest possible IO size. */
  this->buck = ( this->size > 0x1000 ) ? 8192 : this->buck ;
  this->buck = ( this->size > 0x2000 ) ? 16384 : this->buck ;
  this->buck = ( this->size >= 0x4000 ) ? 32768 : this->buck ;
  this->buck = ( this->size >= 0x8000 ) ? 65536 : this->buck ;
  this->buck = ( this->size > 0x10000 ) ? 131072 : this->buck ;
  this->buck = ( this->size > 0x20000 ) ? BLKSZ_ABOVE_128K : this->buck ;

  @b0x1000[this->pooln, logbias[this->lb]]   =
        sum(this->buck == 0x1000 ? 1 : 0);
  @b0x2000[this->pooln, logbias[this->lb]]   =
        sum(this->buck == 0x2000 ? 1 : 0);
  @b0x4000[this->pooln, logbias[this->lb]]   =
        sum(this->buck == 0x4000 ? 1 : 0);
  @b0x8000[this->pooln, logbias[this->lb]]   =
        sum(this->buck == 0x8000 ? 1 : 0);
  @b0x10000[this->pooln, logbias[this->lb]]  =
        sum(this->buck == 0x10000 ? 1 : 0);
  @b0x20000[this->pooln, logbias[this->lb]]  =
        sum(this->buck == 0x20000 ? 1 : 0);
  @bgt128k[this->pooln, logbias[this->lb]] =
        sum(this->buck == BLKSZ_ABOVE_128K ? 1 : 0);
  ++x;
}
tick-1sec /x/ {
  printa("%s,%s,%@d,%@d,%@d,%@d,%@d,%@d,%@d\n",
      @b0x1000, @b0x2000, @b0x4000, @b0x8000, @b0x10000, @b0x20000, @bgt128k
  );
  trunc(@b0x1000); trunc(@b0x2000); 
  trunc(@b0x4000); trunc(@b0x8000); trunc(@b0x10000); 
  trunc(@b0x20000); trunc(@bgt128k);
  x = 0;
}
zil-ios-one-liners.d
dtrace -qn '
BEGIN {totalbytes=0; total = 0;} 
	::zfs_log_write:entry { totalbytes+=args[5]; total++; @ = quantize(args[5]); } 
END { 
    printf("\tTotal IOs: %d Total KBytes: %d\n", total, totalbytes >> 10); printa("\tIO size (bytes): %@d\n", @); }'
    
// IO size through the zil by process ID and execname. Includes both the size of IO,
// and amount of work done per microsecond of time spent in the zfs_log_write function.
dtrace -qn '
  self int64_t bytes;
  self int64_t delta;
  ::zfs_log_write:entry {
    @[pid, execname] = quantize(args[5]);
    self->ts = timestamp;
    self->bytes += args[5];
  }
  ::zfs_log_write:return /(timestamp - self->ts) > 0/ {
    self->delta += (timestamp - self->ts); /* measure delta */
  }
  ::zfs_log_write:return /self->delta > 100000/ {
    /* How many bytes per microsecond? */
    this->delta_us = self->delta / 1000;
    @bytes_us[pid, execname] = quantize(self->bytes/this->delta_us);
    self->ts = 0; self->bytes = 0; self->delta = 0;
  }
  tick-10sec {
    printa("IO Size\npid: %d, name: %s %@d\n", @); 
    printa("Bytes/us\npid: %d, name: %s %@d\n", @bytes_us); 
    trunc(@); trunc(@bytes_us); 
  }'
zil-io-sizes-and-latencies.d
dtrace -qn '
  /*
   * Measure IO sizes and latency through the zil code. This script hides
   * reality through effectively quantizing data to multiples of 4K 
   * numbers, where actual sizes may not be on those exact boundaries. 
   * We can get a sense for how much we are lied to by looking at the total
   * count and comparing against the count given by base2 column, which counts
   * each IO that is actually sized on the bucket boundaries.
  */
  BEGIN {
    printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
            "timestamp","sz.1k","sz.4k","sz.8k","sz.16k","sz.32k","sz.64k",
            "sz.128k","sz.256k","sz.512k", "sz.1m","unk",
            "l1k","l4k","l8k","l16k","l32k","l64k","l128k","l256k","l512k", "l1m","lunk", "mult.4k","total.ios", "throughput");
  }
  ::zfs_log_write:entry {
    /* 
     * this->wt reduces precision down to 1 second, to make result
     * presentation a little bit easier.
     */
    this->wt = (walltimestamp / 1000000000) * 1000000000;
    self->start = timestamp;
    this->sz = args[5];
    this->m4k = this->sz%4096 == 0 ? 1 : 0;

    this->l = this->sz > 0 && this->sz <= 0x400  ? "1k" : 
        this->sz > 0x400 && this->sz <= 0x1000 ? "4k" :
        this->sz > 0x1000 && this->sz <= 0x2000 ? "8k" :
        this->sz > 0x2000 && this->sz <= 0x4000 ? "16k" :
        this->sz > 0x4000 && this->sz <= 0x8000 ? "32k" : 
        this->sz > 0x8000 && this->sz <= 0x10000 ? "64k" :
        this->sz > 0x10000 && this->sz <= 0x20000 ? "128k" :
        this->sz > 0x20000 && this->sz <= 0x40000 ? "256k" :
        this->sz > 0x40000 && this->sz <= 0x80000 ? "512k" :
        this->sz > 0x80000 && this->sz <= 0x100000 ? "1m" :
         "unknown";
    @sz1k[this->wt]   = sum(this->l == "1k" ? 1 : 0);
    @sz4k[this->wt]   = sum(this->l == "4k" ? 1 : 0);
    @sz8k[this->wt]   = sum(this->l == "8k" ? 1 : 0);
    @sz16k[this->wt]  = sum(this->l == "16k" ? 1 : 0);
    @sz32k[this->wt]  = sum(this->l == "32k" ? 1 : 0);
    @sz64k[this->wt]  = sum(this->l == "64k" ? 1 : 0);
    @sz128k[this->wt]  = sum(this->l == "128k" ? 1 : 0);
    @sz256k[this->wt]  = sum(this->l == "256k" ? 1 : 0);
    @sz512k[this->wt]  = sum(this->l == "512k" ? 1 : 0);
    @sz1m[this->wt]  = sum(this->l == "1m" ? 1 : 0);
    @szu[this->wt]    = sum(this->l == "unknown" ? 1 : 0);
    @bucksize[this->wt]     = sum(this->m4k);
    /* IOPS, assuming regular interval sampling */
    @tot[this->wt]    = count();
    /* Throughput assuming regular interval sampling */
    @tput[this->wt]   = sum(this->sz);
  }

  ::zfs_log_write:return /this->sz != NULL/ {
    this->delta = (timestamp - self->start); /* note, this is nanoseconds */
    @l1k[this->wt]    = avg(this->l == "1k" ? this->delta : 0);
    @l4k[this->wt]    = avg(this->l == "4k" ? this->delta : 0);
    @l8k[this->wt]    = avg(this->l == "8k" ? this->delta : 0);
    @l16k[this->wt]   = avg(this->l == "16k" ? this->delta : 0);
    @l32k[this->wt]   = avg(this->l == "32k" ? this->delta : 0);
    @l64k[this->wt]   = avg(this->l == "64k" ? this->delta : 0);
    @l128k[this->wt]   = avg(this->l == "128k" ? this->delta : 0);
    @l256k[this->wt]   = avg(this->l == "256k" ? this->delta : 0);
    @l512k[this->wt]   = avg(this->l == "512k" ? this->delta : 0);
    @l1m[this->wt]   = avg(this->l == "1m" ? this->delta : 0);
    @lu[this->wt]     = sum(this->l == "unknown" ? this->delta : 0);
  }
  
  tick-1sec {
    printa("%Y,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d\n", 
            @sz1k, @sz4k, @sz8k, @sz16k, @sz32k, @sz64k, @sz128k, @sz256k, 
            @sz512k, @sz1m, @szu, @l1k, @l4k, @l8k, @l16k, @l32k,
            @l64k, @l128k, @l256k, @l512k, @l1m, @lu, @bucksize, @tot, 
            @tput);
    trunc(@sz1k); trunc(@sz4k); trunc(@sz8k);
    trunc(@sz16k); trunc(@sz32k); trunc(@sz64k); trunc(@sz128k); trunc(@sz256k);
    trunc(@sz512k); trunc(@sz1m); trunc(@szu);
    trunc(@l1k); trunc(@l4k); trunc(@l8k);
    trunc(@l16k); trunc(@l32k); trunc(@l64k); trunc(@l128k); trunc(@l256k);
    trunc(@l512k); trunc(@l1m); trunc(@lu); 
    trunc(@bucksize); trunc(@tot); trunc(@tput);
  }'
zfs-log-one-liners.d
/* Observe size buckets of IOs through the zfs_log_write function and measure zil_commit latency. */
dtrace -qn '
    ::zil_commit:entry {
        self->zilog = args[0] ;
        self->spa   = args[0]->zl_spa ;
        self->t     = timestamp ;
    }
    ::zfs_log_write:entry {
        this->spa   = args[0]->zl_spa ;
        this->sync  = args[0]->zl_sync == 0 ? "Async" : "Sync" ;
        @x4[this->spa->spa_name, this->sync] =
            sum(args[5] <= 0x1000 ? 1 : 0) ;
        @x8[this->spa->spa_name, this->sync] =
            sum((args[5] <= 0x2000 && args[5] > 0x1000) ? 1 : 0) ;
        @x16[this->spa->spa_name, this->sync] =
            sum((args[5] <= 0x4000 && args[5] > 0x2000) ? 1 : 0) ;
        @x32[this->spa->spa_name, this->sync] =
            sum((args[5] <= 0x8000 && args[5] > 0x4000) ? 1 : 0) ;
        @x64[this->spa->spa_name, this->sync] =
            sum((args[5] <= 0x10000 && args[5] > 0x8000) ? 1 : 0) ;
        @x128[this->spa->spa_name, this->sync] =
            sum((args[5] <= 0x20000 && args[5] > 0x10000) ? 1 : 0) ;
        @xulim[this->spa->spa_name, this->sync] =
            sum(args[5] > 0x20000 ? 1 : 0) ;
    }
    ::zil_commit:return /self->t/ {
        this->sync = self->zilog == 0 ? "Async" : "Sync" ;
        @lathist[self->spa->spa_name, this->sync] =
            quantize((timestamp - self->t) / 1000);
        self->t = 0 ; self->spa = 0 ; self->zilog = 0 ;
    }
    END {
        printa("\n           Pool [%s] zil_commit %s IO record latency%@d\n", 
            @lathist);
        printf("IO Count by Size:\n") ;
        printf("pool     type     %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n",
                "4kb","8kb","16kb","32kb","64kb","128kb",">128kb") ;
        printa("%-8s %-8s %-8@d %-8@d %-8@d %-8@d %-8@d %-8@d %-8@d\n",
                @x4, @x8, @x16, @x32, @x64, @x128, @xulim) ;
    }'

/* Sizes of IOs through the zfs_log_write  */
dtrace -qn '::zfs_log_write:entry {
  this->ts = (walltimestamp / 1000000000) * 1000000000;
  @sz[this->ts, args[5]] = count();
  }
  END {
    printf("timestamp,io.size,count\n");
    printa("%Y,%d,%@d\n", @sz);
  }'
io-size-through-zil-by-file.d
dtrace -qn '
  /* Add a number greater than 0 after the script on the command line, to
   * tell the script how often to print results to screen. If no value is
   * given, script will fail immediately. Value must be positive and is in
   * seconds. Results will be printed "this many" seconds apart, but will
   * be normalized to a per-second result.
   */
  int ts;

  BEGIN { ts = timestamp; }
  ::zfs_log_write:entry {
    this->file_path = stringof(args[3]->z_vnode->v_path);
    @[this->file_path] = quantize(arg5);
    @cnt = count();
    @bytes = sum(arg5);
    bypass_log = `zfs_slog_bypass_ok == 0 ? "will not" : "will";
  }
  tick-$$1sec {
    this->now = timestamp;
    this->delta = (this->now - ts) / 1000000000;
    /* Adjust data to reflect per second values */
    normalize(@, this->delta);
    normalize(@cnt, this->delta);
    normalize(@bytes, this->delta);
    
    printf("----------------------------------------\n");
    printf("Summary:\n");
    printf("IOs >%d(kb) %s bypass log\n", 
          `zfs_immediate_write_sz >> 10, bypass_log);
    printa("IOs through ZIL/sec: %@d\nBytes through ZIL/sec: %@d\n", 
          @cnt, @bytes);
    printf("----------------------------------------\n");
    
    printa("          Filepath: %s\n          IOsize\t\t\t\tIOs/sec %@d\n", @);
    trunc(@); trunc(@cnt); trunc(@bytes);
    ts = this->now;
  }'