備忘録

備忘録

C#で非同期かつ優先度考慮可能なQueueを利用する方法

Ⅰ. はじめに

タイトルの通り「C#で非同期かつ優先度考慮可能なQueueを利用する方法」です。
非同期ではないPriorityQueueは.NET6で実装されました

Ⅱ. やり方

1. 必要なパッケージをNuGetからインストールする
dotnet add package ConcurrentDataStructures --version 0.2.0
2. サンプルプログラムを書く

※簡単の為非同期らしい動きは省きます

using DataStructures;

namespace Test
{
  class Program
  {
    class MyQueue : IComparable<MyQueue>
    {
      public int Id { get; set; }
      public int Priority { get; set; }

      public int CompareTo(MyQueue x)
      {
        // Priorityが大きい順に取得出来るようにする
        return Priority - x.Priority;
      }
    }

    static void Main(string[] args)
    {
      var queue = new ConcurrentPriorityQueue<MyQueue>();
      queue.TryAdd(new MyQueue { Id = 1, Priority = 100 });
      queue.TryAdd(new MyQueue { Id = 2, Priority = 999 });
      queue.TryAdd(new MyQueue { Id = 3, Priority = 200 });

      while (true)
      {
        if (queue.TryTake(out var x))
        {
          Console.WriteLine($"Id: {x.Id}, Priority: {x.Priority}");
        }
        else
        {
          break;
        }
      }
    }
  }
}

実行結果

Id: 2, Priority: 999
Id: 3, Priority: 200
Id: 1, Priority: 100

留意点(2022/08/18追記)

  • 以下プログラムは途中でID5を挿入する。このときID2よりもID5が先に取り出される。
    つまり、CompareToの条件が最優先される。
    (Queueの動きとして妥当では無い気もしますが…)
using DataStructures;
using System;

namespace Test
{
  class Program
  {
    class MyQueue : IComparable<MyQueue>
    {
      public int Id { get; set; }
      public int Priority { get; set; }

      public int CompareTo(MyQueue x)
      {
        // Priorityが大きい順に取得出来るようにする
        return Priority - x.Priority;
      }
    }

    static void Main(string[] args)
    {
      var queue = new ConcurrentPriorityQueue<MyQueue>();
      queue.TryAdd(new MyQueue { Id = 1, Priority = 100 });
      queue.TryAdd(new MyQueue { Id = 2, Priority = 100 });
      queue.TryAdd(new MyQueue { Id = 3, Priority = 999 });
      queue.TryAdd(new MyQueue { Id = 4, Priority = 200 });

      while (true)
      {
        if (queue.TryTake(out var x))
        {
          Console.WriteLine($"Id: {x.Id}, Priority: {x.Priority}");

          if (x.Id == 4)
          {
            queue.TryAdd(new MyQueue { Id = 5, Priority = 100 });
          }
        }
        else
        {
          break;
        }
      }
    }
  }
}

Sheets APIを利用してスプレッドシートに値を書き込む方法

Ⅰ. はじめに

タイトルの通り「Sheets APIを利用してスプレッドシートに値を書き込む方法」です。

Ⅱ. やり方

1. Google Developers Consoleにアクセスする

https://console.developers.google.com/?hl=ja

2. 新しいプロジェクトを作成する

f:id:kagasu:20180114224251p:plain

3. ライブラリを選択する

f:id:kagasu:20180114224350p:plain

4. Google Sheets API を有効にする

f:id:kagasu:20210524213938p:plain

5. 認証情報をクリックする

f:id:kagasu:20180114224554p:plain

6. OAuth クライアント IDを作成する

f:id:kagasu:20210524214111p:plain

7. 「デスクトップアプリ」を選択し、任意の名前をつける

f:id:kagasu:20210524214148p:plain

8. 「クライアントID」と「クライアントシークレット」をメモする
9. サンプルプログラムを書く
var fetch = require('node-fetch');

const clientId = 'xxx-yyy.apps.googleusercontent.com'
const clientSecret = 'xxx'
const redirectUri = 'urn:ietf:wg:oauth:2.0:oob'
const scope = 'https://www.googleapis.com/auth/spreadsheets'

async function getRefreshTokenFromCode (code) {
  const res = await fetch('https://www.googleapis.com/oauth2/v4/token', {
    method: 'post',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      client_id: clientId,
      client_secret: clientSecret,
      code: code,
      redirect_uri: redirectUri,
      grant_type: 'authorization_code',
      access_type: 'offline'
    })
  })

  const obj = await res.json()
  return obj.refresh_token
}

async function getAccessTokenFromRefreshToken (refreshToken) {
  const res = await fetch('https://www.googleapis.com/oauth2/v4/token', {
    method: 'post',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      client_id: clientId,
      client_secret: clientSecret,
      grant_type: 'refresh_token',
      refresh_token: refreshToken
    })
  })

  const obj = await res.json()
  return obj.access_token
}

async function writeSingleCell (accessToken, sheetId, sheetName, cell, value) {
  const url = `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/'${sheetName}'!${cell}?valueInputOption=USER_ENTERED`

  const res = await fetch(encodeURI(url), {
    method: 'put',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      majorDimension: 'COLUMNS',
      values: [ [ value ] ]
    })
  })

  const obj = await res.text()
  return obj
}


(async () => {
  let url = `https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&access_type=offline&scope=${scope}`
  // 表示されたURLにアクセスして手動でコードを取得する
  console.log(url)

  // codeは1回のみ使用可能
  // const code = '4/1A...'
  // const refreshToken = await getRefreshTokenFromCode(code)
  // console.log(refreshToken)

  // refreshTokenをメモする事。
  // 2回目以降はrefreshTokenを利用してaccessTokenを生成する事が出来る
  // const refreshToken = '1//...'
  const accessToken = await getAccessTokenFromRefreshToken(refreshToken)
  console.log(accessToken)

  const sheetId = 'xxx'
  await writeSingleCell(accessToken, sheetId, 'シート1', 'A1', 100)
})()

実行結果

A1に100が書き込まれた
f:id:kagasu:20210524214640p:plain

Androidの共有ライブラリをAndroid以外のOSで利用する方法

Ⅰ. はじめに

AndroidLinuxがベースのOSですが共有ライブラリをLinuxで利用する事が出来ません。

C言語ライブラリの実装が異なる為です。

OS C言語ライブラリ
Linux glibc
Android Bionic libc

この記事ではunidbgを利用して任意のOSでAndroidの共有ライブラリを利用する方法を紹介します。

Ⅱ. やり方

本記事のプロジェクトファイルはこちら

1. 共有ライブラリのサンプルプログラムを書く
#include <jni.h>

extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_hellojni_MainActivity_helloworld(JNIEnv* env, jobject thiz)
{
  return env->NewStringUTF("Hello World!");
}
}
2. 1をコンパイルする
> ndk-build
[armeabi-v7a] Compile++ thumb: helloworld <= main.cpp
[armeabi-v7a] SharedLibrary  : libhelloworld.so
3. 共有ライブラリを利用するサンプルプログラムを書く
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import java.io.File;

class NativeTest extends AbstractJni {
  public void callFunction(){
    var emulator = AndroidEmulatorBuilder
        .for32Bit()
        .setProcessName("com.example.hellojni")
        .build();
    var memory = emulator.getMemory();
    memory.setLibraryResolver(new AndroidResolver(19));
    var vm = emulator.createDalvikVM(null);
    vm.setJni(this);

    var library = vm.loadLibrary(new File("src/main/resources/libhelloworld.so"), false);
    library.callJNI_OnLoad(emulator);
    var result = vm
        .resolveClass("com/example/hellojni/MainActivity")
        // Java VM型のシグネチャ
        // https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/types.html
        .callStaticJniMethodObject(emulator, "helloworld()Ljava/lang/String;");
    System.out.println(result.toString());
  }
}

public class Main extends AbstractJni {
  public static void main(String[] args){
    var nativeTest = new NativeTest();
    nativeTest.callFunction();
  }
}

実行結果

Hello World!

FAQ

Q. unidbgはスレッドセーフですか?

A.いいえ
https://github.com/zhkl0228/unidbg/issues/181#issuecomment-689734652

Q. Java以外はありますか?

A. はい。Python版があります。
また、Unicorn Engineとunidbgの仕様を理解して実装する事でその他言語でも利用可能です。

MariaDBでレプリケーションする方法

Ⅰ. はじめに

タイトルの通り「MariaDBレプリケーションする方法」です。

Ⅱ. やり方

1. Master側の設定を変更する

server.cnf

[mysqld]
server-id = 1
log-bin
expire_logs_days = 3
max_binlog_size = 500M
2. Master側のDBをダンプする
mysqldump -u root -p --all-databases --flush-logs --single-transaction --master-data=2 > dump.sql
3. Slave側の設定を変更する

server.cnf

[mysqld]
server-id=2
log-bin
read_only
4. Slave側のDBをリストアする
mysql -u root -p < dump.sql
5. バイナリログ名とポジション番号を確認する
$ cat dump.sql | grep -m1 "MASTER_LOG_POS="
-- CHANGE MASTER TO MASTER_LOG_FILE='mysqld-bin.000006', MASTER_LOG_POS=387;
6. Slaveを開始する
STOP SLAVE;
CHANGE MASTER TO MASTER_HOST='10.0.0.1', MASTER_PORT=3306, MASTER_USER='root', MASTER_PASSWORD='db_password', MASTER_LOG_FILE='mysqld-bin.000006', MASTER_LOG_POS=387;
START SLAVE;
7. Slave状態を確認する
SHOW SLAVE STATUS\G

以下2点がYesになっていればOK

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

実行結果

Master側のデータがSlave側に反映されている。
f:id:kagasu:20210507211100g:plain

C++で正規表現を利用する方法

Ⅰ. はじめに

タイトルの通り「C++正規表現を利用する方法」です。

Ⅱ. 1回マッチさせたい場合

サンプルプログラム

Source.cpp

// #include <regex>
std::wstring str(L"key01=value02&key02=value02");
std::wregex pattern(LR"((key[0-9]{1,})=([a-z0-9]{1,}))");
std::wsmatch results;
std::regex_search(str, results, pattern);
std::wcout << results[1].str() << std::endl;
std::wcout << results[2].str() << std::endl;
実行結果
key01
value02

Ⅲ. 複数マッチさせたい場合

サンプルプログラム

Source.cpp

// #include <regex>
std::wstring str(L"key01=value02&key02=value02");
std::wregex pattern(LR"((key[0-9]{1,})=([a-z0-9]{1,}))");

std::wsregex_iterator it(str.begin(), str.end(), pattern);
std::wsregex_iterator end;

while (it != end)
{
  std::wcout << "key:" << (*it)[1].str() << std::endl;
  std::wcout << "value:" << (*it)[2].str() << std::endl;
  it++;
}
実行結果
key:key01
value:value02
key:key02
value:value02