Base encoding

バイナリデータの文字列エンコード効率化とか調べていて、普段よく利用するBase64以外にも様々なBase encodingの種類があったので少し調べてみた。

Base16, Base32, Base64

RFC4648で定義されているエンコード形式。

Base16

Base16はいわゆるASCIIコード表での16進数表記というイメージで、1byteを2桁の[0-9A-F]で表現する。データの余白が生じないので、Base32/64のように穴埋めの仕様は存在しない。

Base32

Base32は40bit毎にデータを5分割して符号化し、[A-Z2-7]の文字種で表現する。英字は大文字(あるいは小文字のみ?)、数字と判別の迷う「0」「1」「8」は使用されない。穴埋め文字は「=」。

Base64

Base64は24bit毎にデータを3分割して符号化し、[A-Za-z0-9+/]の文字種で表現する。印刷可能な文字のみで構成されるのは全てに共通する特性で、Base64ではデータ量の増加が33%程度となる。

Base64では「+」「/」といったURLでは別の解釈となりうる文字種が含まれるため(「+」は空白、「/」はパス区切りなどとして解釈されうる)、これらを「-」「_」に置き換えたBase64urlという亜流も存在する。WebアプリケーションなどでURLやパスにエンコードされた情報を含めて受け渡す場合Base64urlを使うことが望ましいが、何故かPHPなどでは標準提供されている base64_encode base64_decode ではBase64urlに非対応のため自分で変換処理を実装する必要が出てくるという悲しみがある。RubyPythonでは urlsafe_encode64 があって平和。

Quoted-Printable

RFC2045で定義されるエンコード形式。

0x3d(「=」)を除く0x21-0x7eまでのASCII文字列はそのまま出力、それ以外は =1c のように「=」に16進表記を並べる形式で出力する。平均では約50%程度のデータ増加量になる。MIMEの仕様の一部であることから分かるようにメール送信などで利用されてたが現在はBase64が主流になっている(と思われる)。

これは別にBase encodingではなかったけれど、なんとなく似たものを思い出したので追記した。

uuencoding

en.wikipedia.org

ついでなので、同様にメール用途などで過去に利用されていたエンコード形式も。

Base64と同様に印字可能な64文字にマッピングするが、英大文字+記号で構成されるためBase64と比較すると可視性が悪く記号も悪さをするのでいろいろと不便。

Base58

ja.wikipedia.org

Base64に対して人間が判別しづらい文字(0Oなど)や「+」などの特殊文字を除外した58文字を利用するエンコード形式。この特性からビットコインリップルなどの暗号通貨のアドレス表現などで採用されているとのこと。

Base85

印字可能な85文字種を使ったエンコード形式でいくつかの種類が存在している。いずれも記号類を多く含むため実用時には区切り文字などとの混在に注意する必要がある。データ増加量は25%。

Ascii85

en.wikipedia.org

 2^{32}(32bit)は 85^{5}で表現可能という理屈から、32bit単位で5bytesにエンコードする。これは他のBase85でも原則として同じ考え方。

Ascii85はエンコード/デコードのロジックが単純明快で、6/7/6/7/6bitに分割した各データに0x33を加算(または減算)するだけで処理が完了する。

Z85

rfc.zeromq.org

Ascii85と比較して「`」「"」「'」「\」などを利用しない文字種が選択されているため、文字列リテラルとしてソースコードなどに混在させやすい性質がある。

A Compact Representation of IPv6 Addresses

datatracker.ietf.org

IPv6アドレスの短縮表現として考案されたエンコード形式。今でも利用されているのか全くの不明。正直読みづらいので流行らなくてよかったのではないかと思う。

base91

base91.sourceforge.net

可用性を最低限残しつつ印字可能な文字種をできるだけ使ってみようという発想がこれか。

データ増加量が23%というのは優秀だけれど、「-」「\」「'」「"」などを含むのでZ85のほうがバランスとしては優れているようにも感じる。

base100

github.com

これは「base100」なのではなく「base💯」なのである。

あらゆる入力データを絵文字に変換するので変換後の表示が実にカオス。Base64よりも高速であるとドヤっているのも面白い。データ増加量など考えてはいけない。

$ echo "the quick brown fox jumped over the lazy dog" | base100
👫👟👜🐗👨👬👠👚👢🐗👙👩👦👮👥🐗👝👦👯🐗👡👬👤👧👜👛🐗👦👭👜👩🐗👫👟👜🐗👣👘👱👰🐗👛👦👞🐁

Base122

github.com

バイナリと文字列変換ということで、もはや可視性などは考慮せずにUTF-8で変換可能な文字と置き換えることで可能な限り圧縮しようという発想。当然だがまともに表示できない文字列になる場合もあるが、それでもvalidなUTF-8文字列なのでよかろうという強い意思を感じる。流石というかデータ増加量はこの中では最も小さい14%。

ネイティブ実装が無いのが何よりの難点という感じではあるが、正直ここまで来るとバイナリのまま扱ってもよいのではないだろうか。

JavaScriptでの実装ではブラウザによってBase64よりも高速の場合と低速の場合があるようで悩ましそう。何より、Base64と比較するとgzip圧縮効率が低下するためWebページでの利用は推奨されないという話。ネタとしては面白い。

base65536

github.com

未使用のコードポイントを利用して圧縮率を高めるという発想でUTF-32に最適化されたエンコード形式。字面のインパクトが大きい。

一応printableという思想は失われていないのか、What makes a Unicode code point safe? @ Things Of Interestを読む限りでは制御文字や空白系、ゼロ幅文字、サロゲートペアや結合文字なども避けられているようである。括弧や引用符に相当するものもないので文字列リテラルとしても利用しやすい。Unicode正規化で壊れたりもしないように考慮があって意外と考えられている気もする。なぜ全力を尽くしたのか。

base2048

github.com

最後の変わり種はbase65536と同じ作者によるTwitterに最適化されたエンコード形式。

Twitterの文字数カウント規則は公式の開発者向けサイトで公開されているが、このルール内で最大限文字数を詰め込むことを目的にした結果、最大385文字を書き込むことができるとのこと。

ここまでやっても日本語などのマルチバイト圏tweetのほうが情報密度高そうなのでチートという感じではある。

おわりに

バイナリデータの文字列エンコードには他にも多くの亜流があるけれど、Base64がデータ効率も特性も実にバランスがよくて納得感だった。組み込みあるいはライブラリ提供されているという観点でも有利だけれど、そういった意味では様々なエンコード形式に対するPythonのカバー率がだいぶ高い雰囲気で面白い(これはWikipedia編集者の知識問題かもしれないけれど)。

独自実装でもよいのでURL safeでもっと効率よい仕組みがあるかと考えていたけれど、普通にBase64urlを使っていくのが大正義であるなあ。

アスパラの塩だれ肉巻き

今日の料理

delishkitchen.tv

この季節は冷しゃぶ用の豚肉がよく売られているのでつい買い溜めてしまう。

肉の大きさに対して余らせるくらいだったので途中から2本ずつ巻くようにして事なきを得たけれど、こうなるのであればアスパラもレシピ通りに4本だけ使って残りを冷蔵するようなことは要らなかった。このあたり料理に慣れていないと機転が効かない。

味付けは塩こしょう中心で薄味だったので、少し醤油を加えたほうが好みの味になるという印象。美味しい。

ひきざんトートロジー

seiga.nicovideo.jp

今連載中のインディーズ作品では一番好きな作品。

向日葵編でシリアス展開に入って、これは途中で更新待ちのもやもや日々を過ごすの辛いと思って読むのを中断していたけれど、めでたくエピソード完結してゆるふわバレンタイン編になったので読むの再開した。やはり最高。

表情とかちょっとした台詞での感情描写が巧すぎるし、登場人物の距離感と関係性もすこぶるよいのであるなあ。万人受けはしないかもだけれど密やかに評価されて欲しい。

Micro Frontends、豚汁、さば味噌煮。

今週読んだ記事

zenn.dev

肥大化するモノリスに対してマイクロサービス化したい要望が出てくるのは、責務が爆発的に増えたフロントエンドについても同様というのは当然の帰結である気はするけれど、実際にどのように分割するのが適切なのかイメージが全く沸かないので採用する動機に欠けている。

バックエンドのマイクロサービス化はコンテキスト境界によってある程度適切に分割できるイメージはあるものの、フロントエンドではページ単位だったりページ内の特定要素だったりで分割することになりそうで、果たしてそれが正しい分割単位でMicro Frontendsのメリットを享受できるのかというとなかなか難しいのではないだろうか。切り出したとして非常に小さな機能だけ分離されて保守コストが増えるような未来がみえる…。

さておき、この記事(書籍)はMicro Frontendsの詳細よりもフロントエンド開発全般を取り巻く環境や技術スタックについて丁寧にまとめられていて非常に勉強になる。

tech.classi.jp

中央DBで管理された排他の考慮されていないPHPジョブシステム、ほぼ同じような構成で悩ましさを持っているので興味深く読む。即時キューと時刻指定のジョブがあるのも同じだけれど、だいたい運用が進むと求められる要件も収束してくる感じか。ある程度一般化できそうなのは面白い。

自前実装で面倒そうに感じていたのはまずロックの部分で、記事内では解説はなかったがここを避けたくてSQSなどに比較的逃げがちではある。

もう1点は時刻指定のジョブの処理だが、現在cronで処理しているのはどうも筋悪だとは思っていたけれど、CloudWatch Eventsをトリガにする以外はcronと考え方は同じなのでこの辺はこうするしかないのかなあという気がした。

設計上の目新しさよりも、クラウドネイティブという点でフルマネージドなAWS各サービスをうまく繋いで実現させているのがよい。AWS Step Functions知らなかったので、あとで勉強する。

tech.classi.jp

組織横断的な支援チームの存在、フロントエンドに限らずバックエンドやインフラでも欲しくなるシーンは多い。似たような体制を作ろうと考えたことはあるけれど、「なんでも聞いてください」という相談役ではなく積極的に組織内の課題を見つけ出して解決していく動きができないといけないので、チームメンバも積極性や関心領域の広さが求められる気がして難しい。

技術力もそうだけれど、組織を変えるという権力をしっかりと使いこなせるリーダーシップが求められて、これを持っている人は非常に少ない。

今週の料理

www.sirogohan.com

急に豚汁が食べたくなったので作った。里芋が美味しい。

このレシピにはなかったけれど、こんにゃく入っているほうが好きな気がしたので次回は入れる。

www.sirogohan.com

鯖は大好物なので無限に食べたい。煮汁をかけすぎた気もするけれど美味しくできた。

白髪ねぎは絶対に添えたほうが美味しい。5分くらい氷水でさらしても丸まらなかったので何かやり方間違っているのかもしれない。

白髪ねぎの作り方/切り方:白ごはん.com

cookpad.com

レシピ通りの分量で野菜を用意すると水が1.5倍程度ないと浸らなくなるので困った。適度に間引いてコンソメも3個にしてなんとか食べられるものにはなったけれど、我が家のティファール18cmソースパンだと限界ギリギリの容量になるので溢れそうになるのが難点。もう少し大きめの鍋が欲しい。

あとは鯖塩焼き食べた。

「質の高い技術文書を書く方法」を読んだ

blog.riywo.com

読んだ。技術記事というよりは業務での技術資料などを書くことはかなり多く、ドキュメントを書くこと自体は嫌いではないのだけれど、どうにも綺麗に整理された文書にはならず自分でも読みづらいと感じることがままあるので参考になる。

全文読んで気になったことなどをメモしておく。これまで意識できていた点、できていなかった点がそれぞれ浮かび上がったので、今後の文書作成の改善に活かしていきたい。

メモ

文書から得られるアウトプットが明確で、読み手のレベルによらず一定に伝わること

とにかく全ての情報を伝えようとして情報量が膨大になり、読み手としてどこを読めばよいのか分からないことが多いので身につまされる。

読み手のレベル感が異なることは理解できているものの、実際にはチームメンバのみに対しても「読み手のレベルによらず一定に伝わる」は実現できていないのではという感じがする。

文書から得られるアウトプットをまず定める

企画提案資料などでは目的や得られる結果について明確にすることは意識的にやっている。 一方で技術文書となると、どうしても正確性や網羅性を重視する結果ドキュメントとして目的が不明瞭なものになりがちなのかもしれない。

そもそもがモチベーションとしては情報共有であるものの、読まれることを前提にしているというよりは自分の中だけにある情報をなるべく記録しておこうという意識のほうが強いので、受け手を意識したドキュメントにならないのは当然だったかもしれない。

冒頭に記載する文書の概要・目的に、このドキュメントを読むことで読み手として何が得られるのかも記載するようにするとよいのかもしれない。

アウトプットは課題の定義から始める

これは比較的得意というか意識することが多いので書けているのではないだろうか。エンジニアリングは全ての行動が課題解決だと思っているので、ここが欠けることはないとは思う。

文書の冒頭では基本的にどのような課題を解決するのかを記載しているけれど、「読み手に欠けているこのような情報を補完する」といった視点でも課題解決を考えるべきか。

読み手を常に想像する

これは殆どできていないので今後の文書作成での大きな課題になりそう。書かれている通り読み手が複数存在しているのは理解していて、チームメンバ、マネージャ、経営層などそれぞれに対して書かれるドキュメントは異なると思っている。

いずれのケースでも伝わるように書くというのは非常に難しいが、自分が意識しているのは文書の上部では解像度が低く、後半に進むに従って解像度が高くなるという書き方をしている。結果として同じような内容を何度も記載することはあるが、経営層向けなどでは序盤のみ読めばよく、詳細を知りたい場合は読む進めれば情報を得られるという点で有用ではないかと考えている。結果として文書量が膨大になりがちという問題はある。

文書の目的を冒頭に簡潔に書く

概要、目的を文書の冒頭に記載するのは自分のフォーマットでも常に採用されている。大事。

とはいえ、後半に進むに従って関連する情報を余すところなく記そうとした結果、冒頭に記載した内容から外れていくことは多い…気がする。

アウトプット(結論)を先に書く

これは全くできていない。

日本語の慣例的な書き方という側面はもちろんあるにしろ、結論に至った経緯がないと説得力を出しづらいと感じてしまう面もあるのかもしれない。後で解決するとはいえ途中でも「何故?」と思われるのが嫌というような心境。

実際にはあまり興味がないという場合もあるし、とりあえず結論は先に書いておくというのはやってみてよいのかもしれない。

昨日取り上げたADR(Architectural Decision Records)のテンプレートではDecisionが後半に配置されていたけれど、これも先に記しておくのがよいのだろうか。

具体的な数値を文章で説明する

結論や考察を文章化するのが難しいので、どうしても数値資料をそのまま貼って読み取ってもらいたい気持ちが出てしまう。結果だけ記載していしても物足りない感じがあるけれど、参考資料として別に切り出すのが正しいのだろうなあ。

計測結果が変わった場合などに、記述が冗長だと複数箇所を更新する必要があって面倒というのもありそう。

箇条書きを濫用しない

これは苦しい。なんならほとんどの技術文書を箇条書きだけで書いているまである。

箇条書きに頼っているので文章構成力が上がらないのかという気もしてきた。

細部にまで目を配る

性格的なものあるけれど、網羅性や情報確度のほうを重視しているのでこれはなるべく気を付けている。

選択肢は可能な限り網羅する

技術選定やUI/UX検討などでは特に記録に残しておきたいと思っているけれど、ここまで記録するともはや破綻するので検討経緯だけは別資料として切り出すことが多いかもしれない。文章ではなくマインドマップのようなもので画像にするのが自分の脳内をアウトプットするには近いのではある。

自分の意見を表明する

これは話として理解できるものの、これまでの体感ではチーム内の立場などで微妙に働くこともありそうな気がしている。チームメンバに意思がない、あるいは同等の検討ができるほど知識がない場合などは特に、提示した意見に引き摺られて有用な議論ができないという懸念があってあえて記載しないようなことをしがち。

なんでも議論したがってしまう悪癖でもあるので、妥当性のある提案はそのまま通してしまってもよいしそうあるべきという気もしている。

記事冒頭にも記載はあったけれど、これは企画資料や作業手順などの文書を主な対象としているのでこれは分かるけれど、技術仕様などには私見などはあまり含める余地がないかな。

長くなりすぎない

これが一番苦手かもしれない。

解像度分割のライン見極めが難しいので全てを同一文書に含めてしまって情報が混在しがち。15〜20分という具体的なボリューム感があるのは参考になる。

まとめ

読み手が何を求めているのか、これを読んで何が得られるのかがとにかく書けていたので、今後の文書作成時には意識していきたい。

この記事がそれを実践できているかというと…技術文書ではないので許されたいか?

Architectural Decision Records

最近、社内の一部プロダクトで導入されたArchitectural Decision Recordsについて。

github.com

文字通りアーキテクチャ決定の経緯について記録することを意味する。

決定の背景と結果が共有されることは、変化するソフトウェア開発において変更の判断や再検討のコストを大きく削減することができるだろう。

Architectural Decision Recordsはドキュメントの形式について規定はないが、テキストファイルとしてプロジェクトのソースコードと同じバージョン管理下に置かれることがよいと思われる。

$ mkdir doc/adr
$ vim database.txt

よく見られるフォーマットでは以下のような項目が採用されるようだ。

  • Title
    • 原則として1ファイルには1つの決定事項のみが記述される
  • Date
    • commit日時で分かるだろうという気もしないでもないけれど、技術選定には時代背景がそれなりに影響するので少なくとも日時が分かる状態であることは望ましい
  • Status
    • 承認された状態は Accepted
    • Proposed の状態でPull Requestを出すことはあるのだろう…が、取り込む場合には Accepted にする??
    • 新しい技術に置き換わった場合は Deprecated などにして新しいドキュメントへのポインタを示すのが良さそう
  • Context
    • 技術選定に関わる背景について事実のみを記載する
    • ビジネス上の事情などでは経営層と現場での感覚の違いによって大いに主観が混ざってしまう部分な気もする
    • 組織の状況が最も影響しそうな感じであるかなあ
  • Decision
    • 決定について記すメインの項目
    • 具体的な判断はここに書くのか他の場所に書くのか迷う
  • Consequences
    • 適用後の状況について、ポジティブ/ネガティブ/どちらでもないいずれの結果についても記す
    • 振り返りは大事

この手のドキュメントとして個人的に求めたいのは、「採用したもの」だけではなく、採用されなかった他の選択肢とその検討についての情報が欲しいと思っている。多くの場合、判断はなんらかのトレードオフの結果として行われるので、結論だけで納得できることはないのではないか。

どこまでの決定を残していくかも判断に迷うところだろう。採用しやすいもの判断が明確なものとそうでないものがあり、後者についてのドキュメントが残りにくい状況になる懸念はある。いくつかのサンプルをメモしておく。

雑感

既に私見を交えながら書いてしまっているがメモ。

新機能やUI検討などの文脈では判断に迷うことが多く決定の経緯を残しておくことが多いが、アーキテクチャについては比較的最適解のような選択肢が狭まることからあまり採用経緯を残してこなかった気がする。実際には既存の仕組みが作り変えられない問題が多く発生するわけで、一因としては設計や技術採用の経緯がないためチーム人員の入れ替わりなどで新しく入ったメンバには安心して作り直せないという気持ちが働くのであろう。テストのないソースコードに似ている。

Architectural Decision Recordsはアジャイル/スクラムの文脈で語られ、透明性の確保を補うものとして機能する。 現実的には検索性の問題や膨大なドキュメントを新人が読むのかという問題はあるにしろ、既存メンバが説明の際に説得力をもつ資料として利用できるのは大きい。

意思決定の合理性をどこまで文章化できるかは、ある程度の情報整理能力や慣れが必要という気がする。漫然と決定だけを記されて判断に困るようなドキュメントが残らないように注意したい。

直近見かけたものでは技術選定の理由としてメンバが完熟しているというものがあり一面的には合理性があるようにも思われたが、別の理由として「時間がないので新しい技術を採用する時間がないため」とも併記されており、途端に合理性が揺らいでくるような印象があった。具体的にどれだけの時間があればチームの開発力やビジネス上のニーズに答えられるものだったのかが明確になっていると納得感が出たのだろうか。難しい。

参考記事

deoplete.nvimとLSP補完での変数名prefix重複解消

年始休み中に古いままだったVim環境を更新して各種補完系をLSP(Language Server Protocol)のプラグインに差し替えた。しばらく快適なエディタ生活を送っていたつもりだったが、時折発生する入力誤りが補完起因であることに気がついた。

違和感があったのは、Rubyインスタンス変数を入力する際に @@value のように @ が二重入力されてしまうこと。そういえば、PHPなどを書いていたときにも $$ のようにprefixが重複する。

補完文字列重複の原因

これには複数の原因があったが、ひとつには補完の仕組みをLSPに移行したことで補完文字列が変化していた。

従来の動作はneocompleteによる補完で、変数名の補完などにはprefix部分は含まれていなかった。ところが、LSP経由で提示される補完候補は基本的に @value のようにprefixを含むものが返ってくるため従来の動作との相違がある。

2点目として、自分の設定ではdeoplete.nvimの設定を公式ドキュメント同様の記述としていた。これは一般的なキーワード文字列にマッチするパターンではあるが、prefixを含まない文字列部分の表現となっている。

call deoplete#custom#option({
    ...
    \ 'keyword_patterns': {
    \    '_': '[a-zA-Z_]\k*',
    \    'ruby': '[a-zA-Z_]\w*[!?]?',
    \ },

deoplete.nvimでは補完開始位置(complete_position)の計算に、特に指定がなければkeyword_patternsを用いる実装となっている(rplugin/python3/deoplete/base/source.py)。このため、入力位置から判定された補完候補はprefixを含む文字列であるのに対して、上記のようなkeyword_patternsの場合はprefixを含まない位置が補完開始位置と判断されるため、prefixmの重複が発生してしまうのであった。

つまり、以下のような流れが発生することになる。

  1. @val まで入力した時点で入力補完を呼び出す
  2. 補完候補として @value が返される
  3. 補完開始位置は keyword_patterns にマッチする val の先頭とされるため、@ の後に @value が補完される
  4. @@value の重複文字列となる

対処

原因が分かれば対処は容易なので keyword_patterns を調整すれば良い。

変数名にprefixがつく言語は、メジャーなものでは Ruby, Perl , PHP くらいだろうか。よく利用するシーンを想定して、一旦以下のような定義に書き換えた。

    \ 'keyword_patterns': {
    \    '_': '\$?[a-zA-Z_]\k*',
    \    'ruby': '@?[a-zA-Z_]\w*[!?]?',
    \ },

Rubyのクラス変数を考えると @* のほうが望ましいか? Perlだと[\$@%] あたりを定義しておいてもよいかもしれないと思ったが、もうPerlは10年以上書いていないので必要になったら考えることにする。

ともあれ、これで変数補完時の重複は解消された。

根本対処は可能か

問題は解決したものの、適切な設定値を指定しないと意図した動作にならないのはエディタの補完機能として頼りない。利用者が意識せずとも快適に動作する環境は作れるだろうか。

補完機能がエディタなどでの利用を想定している場合、補完ロジックを提供する側から置換位置の情報が返されてもよい気がするが、今のところLSPの補完仕様にそのような情報は含まれない(Language Server Protocol Specification - Completion Request)。

deoplete.nvimはsourceのget_complete_positionをオーバーライドすることで補完位置を調整することができるが、LSPの場合は通常LSPクライアント自体がsourceとなるため接続先サーバ毎に異なる挙動を提供することも難しいように思われる。

現時点では最適解を思いついていないので、せめてもの情報源としてこの記事を残した。