Androidのおやすみモード有効にしたら就寝時間以降は画面がモノクロになっておもしろい感じになった
— うたがわきき (@utgwkk) 2021年1月6日
いきなりモノクロになるのでおもしろ体験ができます。
ラーメン屋に来て、おばさん2人組の間の席に案内される。ラーメンを食べていたら両隣から箸をつつかれてつまみ食いされる!! やめてくださいって言っても止めてくれなかったので席を移動したらついて来るし止めてくれない。怒って、ラーメンを床にぶちまけたら2人とも床に箸をやっておいしそうに食べているので、怖くなって店から逃げ出した。
以前こういう記事を書きました。今回はその続きです。
トリップとは、2ちゃんねるなどの匿名掲示板において個人を簡単に証明するための合言葉、またその機能によって表示される文字列のことを指します*1。
Python2までは文字列 str
のエンコードは環境依存でUnicode文字列は unicode
型になる、Python3からは str
がUnicode文字列になる、ということに困っていました。
どう困るのかというと、 crypt.crypt()
が str
型を受け付ける、つまりUnicode文字列しか受け付けなくなってしまいます。トリップを生成するのに使う文字列はShift-JISにエンコードされていないと正しい結果が得られません。
バイト列 bytes
にしてしまえばShift-JISの文字列 (バイト列) を操作できますが、 crypt.crypt()
は bytes
型を受け付けません。Perlの場合は内部表現文字列とバイト列のどちらも crypt
に渡すことができたので困りませんでした。
そうこうしているうちに、Python2はEOLを迎え、2ちゃんねるは 2ch.sc と 5ch.net に分かれてしまいました。
(2021/1/5 21:45 追記ここから)
id:nonylene さんに passlib の passlib.hash.des_crypt.hash
にはバイト列も渡せる、というのを教えてもらいました。やはりC拡張を書く必要はなかった!!!
全ての詳細をすっ飛ばすと、これだけでやりたいことが実現できます。
from passlib.hash import des_crypt # 中略 tripkey = tripstr[1:] # treat as Shift-JIS bytes tripkey = bytes(tripkey, encoding='shift-jis') salt = (tripkey + b'H.')[1:3] salt = re.sub(rb'[^\.-z]', b'.', salt) salt = salt.translate(bytes.maketrans(b':;<=>?@[\\]^_`', b'ABCDEFGabcdef')) trip = des_crypt.hash(tripkey, salt=salt.decode('shift-jis')) trip = trip[-10:]
passlibのコードを見ると*2、Python3ではUnicode文字列しか受け付けなくなったのでpure Pythonな実装にフォールバックします、といったコメントが書いてあります。 crypt(3)
には char *
を渡せばよいなら文字コード関係なくバイト列でいいはず、と思っていたのですが、どうやら間違っていなかったようです。
ということで、以降に書いてあるC言語のコードは必要なくなりました。よかったですね。
(追記ここまで)
Pythonの crypt.crypt()
ならびにPerlの crypt
は、crypt(3)
を実装したものです。
そして、Python3でもバイト列は種々の文字コードでencodeできます。
ということは crypt(3)
にバイト列をもとにした文字列 char *
を渡せるインタフェースがあればよさそうですね。
軽く検索した感じでは bytes
を渡せる crypt(3)
のPython実装は見つかりませんでした*3。
ということでPythonのC拡張を書きます。C拡張は書いたことがあるので、サクッとできるつもりでした。結局、C拡張内で関数の引数をパースするのがうまくいかなくてCPythonのcryptモジュールの実装をコピペしてきたのですが……。
ともあれ crypt.crypt()
の実装の本質はこのあたり*4*5なので、あとは bytes
型を受け付けて char *
に変換するように書き換えてやればよさそうです。
バイト列を受け付けるように書き換えた crypt.crypt()
の実装 bytecrypt.crypt()
*6がこちらになります*7。エラーハンドリングが雑なので、このまま実用するのは危険です。
static PyObject* bytecrypt (PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; const char *word, *salt, *crypted; struct crypt_data data; word = PyBytes_AsString(args[0]); if (word == NULL) { goto exit; } salt = PyBytes_AsString(args[1]); if (salt == NULL) { goto exit; } memset(&data, 0, sizeof(data)); crypted = crypt_r(word, salt, &data); if (crypted == NULL) { return_value = PyErr_SetFromErrno(PyExc_OSError); goto exit; } return_value = PyBytes_FromString(crypted); exit: return return_value; }
あとはこの bytecrypt.crypt()
を使ってやれば完成です*8。
>>> generate_trip('#istrip') '◆/WG5qp963c' >>> generate_trip('#ニコニコ') '◆pA8Bpf.Qvk'
2バイト文字*9や半角カナを含む入力に対しても正しく出力できていそうですね*10。
>>> generate_trip('#t%{rシ)L,') '◆HAckEr.Tac' >>> generate_trip('#DRL諞Qq@') '◆AAAAAAAc.s' >>> generate_trip('#JM@/!詫8') '◆GoGoGO/Bos' >>> generate_trip('#s0ンX[aF-') '◆1uzee/wmbQ' >>> generate_trip('#ゥ.N避y承') '◆4/9.......' >>> generate_trip('#DRL諞Qq@') '◆AAAAAAAc.s'
何度かリンクを貼っていましたがリポジトリはこちらです。
*2:https://foss.heptapod.net/python-libs/passlib/-/blob/branch/stable/passlib/handlers/des_crypt.py#L221-222
*3:ここで見つかっていればC拡張を書く必要はなかったです。実はあるよ! とか誰か知ってたら教えてください
*4:https://github.com/python/cpython/blob/f7f0ed59bcc41ed20674d4b2aa443d3b79e725f4/Modules/_cryptmodule.c#L33-L49
*5:https://github.com/python/cpython/blob/f7f0ed59bcc41ed20674d4b2aa443d3b79e725f4/Modules/clinic/_cryptmodule.c.h#L23
*6:bcrypt にすると名前が被ってややこしい
*7:https://github.com/utgwkk/20210103-sketch-tripcode/blob/094fbca7c9a6e21d1381ed820d69f6e6305b3cc5/bytecrypt.c#L9-L34
*8:https://github.com/utgwkk/20210103-sketch-tripcode/blob/094fbca7c9a6e21d1381ed820d69f6e6305b3cc5/tripcode.py#L7
*9:Unicode時代になってから2バイト文字って聞かなくなりましたね
*10:テストケースに用いた入力は トリップ倉庫 - (^p^)レア酉集積所(^q^)@Returns【12/29更新】 - atwiki(アットウィキ) を参照しました
講義で、N次方程式の解に関する定理を習った。2つ等式があったけどどっちも偽だと思う。
2018年の中ごろまで動かしていて、Twitter Streaming APIの終了と同時に稼動停止した画像検索・閲覧システムだったけど、年末年始やることがなかったのでReactで書き直すことにした。
書き直して再稼動させるというわけでもなく、Reactに再入門するためぐらいのモチベーション。TODOアプリケーションや三目並べを書いてもReact入門はできるかもしれないけど、自分にとって馴染みがあるトピックのほうがとっつきやすそう、という感じ。
<script>
タグでVue.jsを読み込む、コンポーネントの分割はなく全てがベタッと書いてある、jquery-lightboxを読み込んでいる、という状態だったのをもうちょっとましにできたらゴール。
とにかく高速に始めたいのでcreate-react-appで作業場所を用意した。
--template typescript
引数を渡すとTypeScriptで書き始められる。とにかく高速に、と言いつつESLintやprettierの設定はひと通り済ませた。
$ npx create-react-app sukui-front --template typescript
クラスコンポーネントを一切書かずにReact hooksだけで書いてみたけど、今のところは変なつまずきもなくてよさそう。画像一覧に関する情報を useImages()
っていうcustom hookに集約させる、みたいな書き方ができている。
初回render時にだけAPIを叩くときは useEffect(() => { doAPI(); }, [])
ってやればいいのだろうか。ミスったときはすごい勢いでAPIリクエストが行われていて、外部サーバーに向けていなくてよかったね~という感じになる。
ページ送りボタンとか、検索フォームとか、アプリケーションのいろいろなところから画像に関する情報を取得・操作することになる。Reduxとか使えばいいのかもしれないけど、いろいろな概念を一気に習得しようとすると道が長くなるのでとりあえず完成させることを目標にした。
リポジトリはこちら。