これからのJavaとの付き合い方を考える

はじめに

イメージ・マジックの安藤です。
皆さんは、GWはどのように過ごされたでしょうか? 私はひたすら配信サイトで映画を探して見ていました。小さい頃に何となくテレビで見た映画を改めて見るのもまた面白いものだと感じています。


さて、今回はユーザーエンドのJava、いわゆるPublic JREと近年のJavaを取り巻く環境の変化についての話です。
話の大元となるのはこちらの記事です。

JDKのリリースモデルとライセンスの変更

Java9リリース後、JDKは9月と3月の6ヶ月ごとにリリースされるようになりました。Oracleによれば、大きな機能の進捗に引っ張られて中小機能のリリースが滞ることを危惧してとのことです。
JDKのライセンスも変更になっています。ライセンスはBCLからGPL v2.0に変更されており、加えてOracle JDKとOpenJDKの機能的な違いはJava11からはなくなりました。Oracleが配布するJDKが終了することばかりが注目されていますが、この変更の本質はJRE/JDKを容易に再配布可能にすることにあるようです。
Oracleは、現在のJavaが「JREをPCにインストールして画一的に実行する」方式であることにいくつかの弊害があるとしており、JREをアプリケーションにバンドルするための方式の整備を進めています(Java9で新たに登場したモジュールもそのための改善の一環です)。

Java11以降のPublic JREの廃止と新たなアプリケーション配布方式

前述の通り、OracleはJREのアプリケーションバンドルを徐々に推し進めています。そのような中で、Java11からはPublic JRE、つまりJavaの公式サイトで入手できるJREは廃止されています。現在配布しているのはJava8ですが、個人使用のユーザに対しての無償アップデートは2020年12月末まで、それ以外の無償サポートは2019年1月に終了しました。
Java11ではjlinkというバンドル用JREを作成する機能が付いています。アプリケーションに必要なモジュールを抽出するためのjdepsという機能も付いています。今年9月にリリースされるJava13にはjarからマルチプラットフォーム向けのアプリケーションやそのインストーラを作成するjpackageという機能も追加される予定です。
バンドル用JREの作成に際してはこちらのサイトを参考にさせていただきました。

業務での方針を考える

弊社で開発中のアプリケーションではPDFの帳票印刷にJavaを使用しています。現在は実行可能JarをPCにインストールされているJavaで実行していますが、JREを配布して実行できるような仕組みを追々作成していく必要があるでしょう。 テストとしてバンドル用のJREを作成してみましたが、40MBほどの容量になりそうです。
配布するために一度圧縮する必要やアップデートの方法など検討事項がいくつかありそうなので少しずつ進めていく予定です。

DLL+EXEからDLLの使い方を解析する[後編]

はじめに

イメージ・マジックの安藤です。
今年は雪が降ることはないのかなと思っていたらまさかこの時期にとは思いもしませんでした。ともあれ、平日でなくて良かったと思っています。私は地元が北海道なのですが、そちらは今年も順調に雪が積もっているようでした。
今回は前回の話の続きです。

あらすじ

前回はコンパイル時にDLL内のメソッドを暗黙的にリンクするまでの手順を説明しました。今回はメソッドの引数と戻り値の解析です。

.NETアプリケーションを逆コンパイルする

JavaやC#などコンパイル時に中間言語に翻訳されるような言語は、特殊な対策が施されていない限りかなり可読性の高い状態で逆コンパイルすることが可能です。 .NETアプリケーションを逆コンパイルするソフトはいくつかありますが、今回はILSpyというソフトを使用しました。

データのマーシャリング

逆コンパイルしたソースからDLLのメソッドの使い方がわかれば万々歳で全てが解決するかと言われるとそんなことはありません。.NET Frameworkにはマネージコードという概念があり、その外にあるコード(アンマネージコード)を呼び出すにはデータのマーシャリング(変換)が必要になります。
つまり、C++で使われると型とC#で使われる型はかなり違うということです。
大体の変換対応表はMSの公式ドキュメントに記載されています。
この中でも特にマルチバイトの文字列はC++で扱うのはだいぶ辛いです。それから、上記のドキュメントには書いていませんが、関数ポインタを渡したいときはdelegateが使われたり、参照(例えばWIN32APIにあるHANDLE型の参照はLPHANDLE)を渡したいときはrefキーワードが使われたりします。ちなみに、System.IntPtrとref System.IntPtrは別物です。

おわりに

やってみて思いましたがDLLの解析はノーヒントだとだいぶ辛いですね(探してもあまり情報が出なかったので)。なのでその軌跡をここに残すことにしました。

DLL+EXEからDLLの使い方を解析する[前編]

はじめに

イメージ・マジックの安藤です。
先日から季節が急速に真冬の様相を呈していますね。風邪やインフルエンザが流行りだす時期なので皆様におかれましても十分ご用心ください。ちなみに、私は去年の大晦日から今年の三が日にかけてインフルエンザを発症するという何とも言えない新年スタートを切っているので気を付けます。
今回は題名の通りDLLの解析をしたという話です。

DLLとは

多分この説明要らないんじゃないかなぁと思いつつ書いておきます。
DLLとは、プログラムのある機能を再利用できるようにプログラム本体と切り分けたライブラリのことを指します。Windowsならば.dll、Linux系では.soという拡張子が使われます。
なお、今回の話はWindowsの.dllで、一般的な、Cで記述されたDLLという話です。

コンパイルを通すのに必要なもの

DLLの利用方法には暗黙的リンクと明示的リンクが存在します。今回は完全に個人的にDLLを使いたいだけなので暗黙的リンクでコンパイルするものとします。
以下が必要になります。

  • インポートライブラリ(.lib)
  • ヘッダファイル(.h)
更にメタ的に言うとこうなります。

  • エクスポートされている関数名
  • 関数の引数と型

初期状態

DLLがどんな状態で使われていたのかを少しだけ書いておきます。
DLLとDLLの機能をラップしたEXEがあり、EXEに引数を渡して実行してあげるとDLLの機能が使えるという構成になっていました。最終的にはこのEXEを介さずにDLLの機能を呼び出すのがゴールです。

インポートライブラリを用意する

第一の関門として、インポートライブラリが存在していません。ただ、これに関してはVisual Studioを使えばDLLから同等のものを用意することができます。
まず、以下のコマンドを実行します。

dumpbin /exports hoge.dll


C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools>dumpbin /exports VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll
Microsoft (R) COFF/PE Dumper Version 14.16.27024.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll

File Type: DLL

  Section contains the following exports for c1.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          1    0 000DDDB0 AbortCompilerPass
          2    1 000DDE50 CloseTypeServerPDB
          3    2 000055FC InvokeCompilerPassW

  Summary

       32000 .data
       17000 .pdata
       F3000 .rdata
        7000 .reloc
        1000 .rsrc
      138000 .text

このコマンドでDLLからエクスポートされている関数の一覧がテーブル上に表示されます。MSVCに存在する適当なDLLを表示してみます。 ここでordinal~nameの列に注目すると関数名が分かります。この関数名を以下のように羅列し、拡張子を.defで保存します。これはモジュール定義ファイルと呼ばれるものです。

EXPORTS
AbortCompilerPass
CloseTypeServerPDB
InvokeCompilerPassW

作成したモジュール定義ファイルは以下のコマンドでインポートライブラリに変換できます。なお、64bitアーキテクチャを想定しています。

lib /DEF:hoge.def /MACHINE:X64 /out:hoge.lib

ヘッダファイルを用意する

ヘッダファイルでは以下のように書くことでDLLの関数を宣言できます。

#define DLLImport __declspec(dllimport)

#ifdef __cplusplus
extern "C"{
#endif

    DLLImport void funcName1();
    DLLImport int funcName2(int arg);

#ifdef __cplusplus
}
#endif


以上を見るとわかりますが、関数の戻り値と引数がわからないとDLLの機能は使えないことが分かります。

以降、今回のケースで関数定義をどう解析したかは次回の私の番で書いていきたいと思います。

僕らの96bit戦争

はじめに

こんにちは、イメージ・マジックの安藤です。

先日の台風24号で私の住んでいるアパートは何度も大いに揺らされ、今にも倒壊するのではないかという勢いでした。
本当に大変だったのは翌日の電車でしたね。私の使用している沿線は早朝の段階では止まっていました。

そんなわけで、今回の話題は文字表現とビット演算の話です。

情報を管理する媒体について

当社で日々製造されている製品は、実際にお客様のお手元に届けられるまでに実に様々な工程を経ています。
しかも、それらは大量生産されているようなものばかりではなくお客様ごとのオンデマンド製品も多分に扱っています。
これらの製品を間違いなくお客様にお届けするためには、注文や製品ごとに情報をきっちりと管理していく必要があります。
そしてこれらの製造を支える在庫の管理・調整も効率的に向上を回していくうえで欠かせないものです。

そこで重要になってくるのが情報を管理する媒体です。最も簡単に実現できるものとしては、バーコードを印刷したラベルシールがあります。

ところで、皆様はユニクロのセルフレジを利用したことがありますでしょうか?
商品を置くだけで何をどのくらい買うのかを瞬時に判断してくれるので初めて使われた方は驚かれるかと思います。

実は、このレジにはRFID(非接触型ICタグ)という仕組みが使われており、一度に複数の商品を読み取ることができます。

当社においても、RFIDを実用していくために現在様々な検討をしています。

RFIDに書き込む、RFIDを読み込む

RFIDにはEPC Class1 Generation2という規格があり、4つのメモリバンクで構成されています。

  • RESERVED
  • EPC
  • TID
  • USER

データを読み書きする部分は主にEPCメモリで、通常は96bitを使います。USERメモリも自由に読み書きできますが、使用するチップによっては実装されていないこともあり容量も特に決まっていません。
TIDメモリはRead Onlyで32bit以上のチップの識別情報が書き込まれています。

一言でまとめますと、96bitに書きたい情報を詰めろということです。

96bitの使い道を考える

96bitでできることは何か考えてみましょう。

まず、これをビットボートとして扱った場合は96の状態を表現できますが、RFIDの使い道としては微妙すぎます。

次に、考えることとしては数字として使うことでしょう。32bitの整数であれば3つ分表現できますが、やはり不便です。

そこで行き着くものとしては、やはりASCII文字になるかと思います。この場合、1文字が8bitで最大12文字まで使用できます。

ところで、ここで一つ問題があります。
日本で使われているバーコードの規格であるJANは13桁で規定されているのです。もっとも、13桁目はパリティビットとなっているため導出が不可能というわけではないですが様々な場面で余計な手間がかかってしまいます。

様々な検討の結果、最終的にASCIIの余計な文字を削って16字まで表現できるようにするという方式を取ることにしました。

96bitで16文字を表現する

96bitで表現できる文字数を増やすには、1文字に使用できる選択肢を減らせばいいです。
1文字を6bit(=64種類)とすると96/6=16となり16文字分使えることとなります。
この際、64文字は好きなように選ぶことができます。

今回は0-9A-Zの半角英数大文字と括弧など一部の半角記号を除いた55文字を採用することとしました。

エンコード/デコードを実装する

諸般の事情により、上記の実装はnode.jsで行いRFIDリーダライタとのやり取りはネイティブモジュールを挟むことで実現しています。

読み取られたデータは16Bのデータとなっており、最初の4BはTIDです。
デコード時は12Bを6bitごとに分解する必要があり、エンコード時は最大16文字を6bitで表現し12Bに詰めなおす必要があります。

const codeMap = []; // 省略 最大64種類の文字に置き換えられる

const bitMap = [1, 2, 4, 8, 16, 32, 64, 128];

const calc = arr => arr.map((n , i) => n ? bitMap[i] : 0)
    .reduce((sum,  n) => sum + n,  0);

const toBit = (b, arr = [], index = 0) => index === 6 ? arr :
    toBit(parseInt(b / 2, 10), arr.concat(b % 2), index + 1);

const encode = src => {
    const chunk = src.split('') // 1文字ごとに分ける
        .map(s => toBit(codeMap.indexOf(s)))
        .reduce((a , b) => a.concat(b), []);
    return Array(12).fill(0)
        .map((_, i) => calc(chunk.slice(i * 8, (i + 1) * 8)));
};

const decode = src => {
    const chunk = src.slice(4) // TIDをスキップ
        .map(b => Array(8).fill(0).map((_, i) => b >> i & 1))
        .reduce((a , b) => a.concat(b), []);
    return Array(16).fill(0)
        .map((_, i) => codeMap[calc(chunk.slice(i * 6, (i + 1) * 6))])
        .join('');
};

おわりに

エンコード/デコードの実装が意外と面倒で2時間ほどかけてしまいましたが、これで16文字の範囲で自由にRFIDを書き換えることが可能になりました。
ちなみにネイティブライブラリは当社別メンバーの力作です。

参考

20160129-UHF帯RFID標準コード体系ガイドライン初版

PHPにおける配列作成・分解マジック

はじめに

こんにちは、イメージ・マジックの安藤です。

オフィスに置いてあるウォーターサーバーを最も頻繁に使用している気がします。水が美味しいのは素晴らしいことです。

さて、今回は現在開発しているシステムで採用されているPHP7に関して、配列の中身を複数の変数にしたり複数の変数を配列にしたりする方法についてです。

PHPにおける配列とは

本題に入る前にPHPでの配列の扱いについて軽く触れておきます。

PHPの公式リファレンスには配列について以下のように説明されています。

PHP の配列は、実際には順番付けられたマップです。マップは型の一種で、 キーに関連付けます。

つまり、PHPには厳密には連想配列しか存在しないということです。

配列のキーには数字(整数)か文字列のみ使用でき、「数字として妥当な文字」がキーに指定された場合に数字に変換されるなどの変換法則がいくつかあります。

詳しくは公式リファレンスをご覧ください。

配列の作成

基本的にarray()を使用することで任意の配列を作成することが可能です。

PHP5.4からは[]を使うこともできます。

$data = [
 [1, 'noel'],
 [2, 'michael'],
];
$data = [
 ['id' => 1, 'name' => 'noel'],
 ['id' => 2, 'name' => 'michael'],
];

既に連想配列の値が変数として用意してあり、変数名と同じキーの配列を作りたい場合はcompact()を使うことができます。

$company = 'IMAGE MAGIC';
$state = '東京';
$city = '千代田区内神田';
print_r(compact('company', 'state', 'city'));

Array
(
  [company] => IMAGE MAGIC
  [state] => 東京
  [city] => 千代田区内神田
)

配列の分解

PHP5.xやPHP7.0では通常の配列はlist()、連想配列はextract()を使用することで実現できました。

$data = [
  [1, 'noel'],
  [2, 'michael'],
];
// $id = 1, $name = 'noel'
list($id, $name) = $data[0];

foreach ($data as list($id, $name)) {}

$data = [
  'company' => 'IMAGE MAGIC',
  'state' => '東京',
  'city' => '千代田区内神田',
];
/*
 * $company = 'IMAGE MAGIC';
 * $state = '東京';
 * $city = '千代田区内神田';
 *
*/
extract($data);

PHP7.1ではlistの代わりに[]を使うことができます。

$data = [
  [1, 'noel'],
  [2, 'michael'],
];
// $id = 1, $name = 'noel'
[$id, $name] = $data[0];

foreach ($data as [$id, $name]) {}

$data = [
  'company' => 'IMAGE MAGIC',
  'state' => '東京',
  'city' => '千代田区内神田',
];

/*
 * $company = 'IMAGE MAGIC';
 * $state = '東京';
 * $city = '千代田区内神田';
 *
*/
[
  'company' => $company,
  'state' => $state,
  'city' => $city,
] = $data;

そのほか便利な操作

  • array_keys
    連想配列のキーだけを取り出して配列にする
  • array_values
    配列を0からの添え字の配列にする
  • array_key_exists
    指定したキーが配列に存在するか調べる
  • in_array
    指定した値が配列に存在するか調べる

おわりに

PHPでは配列が作りやすいので色々と便利ではありますが、mapやfilter,reduceなどが使いにくいというが少々残念なところです。
省略できる記法は省略して可読性を上げていきたいですね。

Immutableという概念について

はじめに

こんにちは、イメージ・マジックの安藤です。

社会人歴は2年目に突入し、入社して1ヶ月ほどが過ぎました.

日々バグ対応を行っているのですが、弊社のシステムに関する私の理解がまだ浅いので苦戦することも多々あり、そこが仕事の面白みかなともまた思っている次第です。

今回はコードを書く上でのワンポイントとしてImmutableという概念について少し触れればと思います。

なお、以下サンプルコードはJavaScriptで記載します。

Immutableとは

ある値がImmutableであるとはどういうことでしょうか?

この記事では、「ある値の状態が変わることがない」ということをImmutableとしたいと思います。

普通に考えると、Immutable=不変なので値が変わらないということから定数のようなものを思い浮かべるかもしれませんが、その違いとは何でしょうか。

まずは単純に数字同士の足し算を考えてみましょう。

数字同士の足し算はすべてImmutableになり、以下の例ではnum1やnum2はconstなので直接書き換えることもできないためこれらの値の状態が変わることはありません。

const num1 = 1;
const num2 = 3;
const num3 = num1 + num2;
console.log(num1, num2, num3); // 1 3 4

では、値の状態が変わる(mutable)場合の例を見てみましょう。

以下ではarrはconstとなっていますが、sort関数で昇順にソートすることでarr自体が変更されていることがわかるでしょうか。

const arr = [1, 3, 2, 5, 4];
arr.sort((a, b) => a - b);
console.log(arr); // [1, 2, 3, 4, 5]

何故このようなことが起こるのかというと、少し難しい話になりますが、constはオブジェクトのインスタンスが変わらないことしか保証しないからです。

上記の例ではarrはArrayクラスのインスタンスになっており、オブジェクトの内部で状態が変更されています。

そして、arrが見ているインスタンス自体は変更されていないため、arrがconstであるかどうかに関わらず値の状態を変更することができてしまいます。

余談ですが、sortのような呼び出したオブジェクト自体が変化するメソッドを破壊的メソッドと呼びます。

なお、同じArrayクラスでも以下のように破壊的でないものもあります。JavaScriptに限らないですが、自分の使おうとしているメソッドが破壊的か否かはきちんと確認するべきでしょう。

const arr = [1, 2, 3, 4, 5];
const added = arr.map(n => n + 1);
console.log(arr, added); // [1, 2, 3, 4, 5] [2, 3, 4, 5, 6]

最後に、Immutableな配列への要素追加とmutableな配列への要素追加の対比の例を載せておきます。

const arr = [1, 2, 3, 4];
const arr2 = arr.concat(5); // immutable add
console.log(arr, arr2); // [1, 2, 3, 4] [1, 2, 3, 4, 5]
arr.push(5); // mutable add
console.log(arr, arr2); // [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]

Immutableであるメリットとは

ここまでImmutableとは何かということに触れてきましたが、Immutableであることのメリットに触れていきたいと思います。

Immutableであることの最大のメリットはオブジェクトの破壊的変更を気にする必要がなくなるという点に尽きます。これはプログラムを書いていくうえで想定外の変更が起きにくくなり、バグを抑制することができるということでもあります。

また、Immutableを適切に意識することでプログラム上でどの値がどのように変更されていくのかが追いやすくなるという効果もあるでしょう。

Immutabeで注意すべき点

Immutableにしようとする値の状態が頻繁に変更される場合や変更可能なデータを多く持っている場合、Immutableは適切でないことが多いため注意が必要です。

また、非破壊的メソッドを使っているとメソッドチェーンを繋げやすくなりますが、過剰なメソッドチェーンは可読性を下げることになるので注意しましょう。

参考

Immutableの利便性、大きなメリットについて。

イミュータブル – Wikipedia