ダウンロードした tar.gzをファイルに書き出さずに展開する

諸事情で Perlを書いていてやり方がわからなかったので調べた. ローカルファイルの tar.gzを展開みたいなのはそれなりにあったけど, オンメモリのまま処理する方法が皆無だった.

#!/usr/bin/env perl
use strict;
use warnings;

use HTTP::Tiny;
use Encode;
use Archive::Tar;
use IO::Uncompress::Gunzip qw(:all);

my $dest = shift;
my $url = 'https://www.cpan.org/src/5.0/perl-5.36.0.tar.gz';

my $res = HTTP::Tiny->new->get($url);
unless ($res->{success}) {
    die "$res->{status} $res->{reason}";
}

my $tar_buf;
gunzip \$res->{content}, \$tar_buf or die "gunzip failed: $GunzipError\n"; 

open my $fh, '<', \$tar_buf or die "Can't open tar content as a file handle: $!";

my $tar = Archive::Tar->new;
$tar->read($fh);

if (defined $dest) {
    $tar->setcwd($dest);
}

$tar->extract;

close $fh;

Perl Navigator を使って書いたけど, 他言語に比べるとだいぶ貧弱だが, 幾分補完やエラーチェックも効き Emacs等で書くよりはだいぶ楽に書けた気がする. 何も補完が効かないとかさすがにもう書けなくなってしまったのでありがたい存在でした.

git-credential-manager-coreのインストール場所が Git for Windows 2.36.1で変更されていた

WSL2の git認証の手間を省くために git-credential-manager-coreを使っているが, Git for Windows 2.36.1でインストールパスが変わっており, pushや pullをしたりしようとすると file not found と出るようになってしまった. パスは次のように変わっていました.

C:/Program Files/Git/mingw64/libexec/git-core/git-credential-manager-core.exe

から

C:/Program Files/Git/mingw64/bin/git-credential-manager-core.exe

に変わっていた. 古い記事を参照しようとするとハマるので注意.

変更が行われた PRはこれ.

github.com

WSL2内の .gitconfigを下記のように変更しておいた.

[credential]
helper = "/mnt/c/Program\\ Files/Git/mingw64/bin/git-credential-manager-core.exe"

emacs 28.1で導入された repeat-modeに関する調査

はじめに

最近まともに追っていなかったから存在さえ知らなかったのだけど、markdown-modeの issueでそういう機能があってどういうものかと使い方を調べてみた.

機能

特定のコマンドのグループを連続でお手軽に実行するための仕組み. 例えば移動系コマンドで次に移動が C-c n , 前に移動が C-c p みたいな場合毎回 C-c n, C-c pと叩くの面倒くさい。一方的に進む、戻るのであれば数値プレフィックスがあるが Vimのように数だけ押せばいいとわけじゃないし、進んだり戻ったりと繰り返したい場合などは使えない。そこで使えるのが repeat-moderepeat-modeを使って初回実行だけは C-c nだが以降は n, pでそれぞれ移動するということが実現できる.

myuheさんが作られた smartrep.elに近い.

sheephead.homelinux.org

使い方

  1. repeat-mode を有効にする
  2. キーマップを定義する
  3. 起点となるコマンドの repeat-map 属性に定義した keymapを設定する

repeat-mode を有効にする

;; global minor modeです
(repeat-mode +1)

グループで使いたいコマンド郡のキーマップを定義する

(defvar git-gutter-repeat-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "n") 'git-gutter:next-hunk)
    (define-key map (kbd "p") 'git-gutter:previous-hunk)
    map))

コマンドの repeat-map属性を設定する

(put 'git-gutter:next-hunk 'repeat-map 'git-gutter-repeat-map)
(put 'git-gutter:previous-hunk 'repeat-map 'git-gutter-repeat-map)

実行例

repeat-mode起点となるコマンドを実行すると minibufferにRepeat with` と表示され, 該当するキーマップが有効になる.

おわりに

導入されたばかりということもあってか標準パッケージではほとんど使われておらずごく一部のモードで使われていませんでした。標準ライブラリで汎用的な機能を提供してくれるのはありがたいけど、どのコマンドをグルーピングするかとかは好みによるところもありそうなのでライブラリ側で設定すべきなのか個人で設定すべきなのかまだよくわかっていない。個人で設定する場合は smartrepなどに比べるとコード量が多くそれほど良いものではないのかなという気もしている。今後の使われ方を見て markdown-modeでどうするかなというのを考えていきたいかな。

LinkedIn Learningの Rust Code Challenges をやった

www.linkedin.com

課題がいくつかあって解いて解説を見てという感じのコース。コードの雛形は githubに用意されているので, 足りていないところ埋めてテストが通れば OKという感じ.

かっちりしたコースではなく解説はわりとグダっていたり雛形のコードに微妙に問題があったりというケースがあったのでクオリティが高いものとは決して言えなかった. でもまあ 3rd partyライブラリを使ったり, そこそこ実用的なものもあり Rustで何を書いたらいいかわからんという自分には少しプラスになったかなとは思った. Rustの基本を習得してその後何か少し発展的なものを書いてみたいという人にちょうどいいぐらいかなと思った。

Ubuntuで VSCodeの Quick Fixが使えなかった問題の調査と修正

VScodeには Quick Fixという機能があって, Rustとか Goの traitとか interfaceを実装する際の雛形の生成に使えるのですが, そのショートカットである Ctrl + .Ubuntuで効かなかったのでその原因の調査と修正を行った.

バインドされているキーを探す

gsettings list-recursively を使ってショートカット一覧を出力し, Ctrlがバインドされているキーを探す.

% gsettings list-recursively | grep "<Control>" 
org.freedesktop.ibus.panel.emoji hotkey ['<Control>period', '<Control>semicolon']
org.freedesktop.ibus.panel.emoji unicode-hotkey ['<Control><Shift>u']
org.gnome.Terminal.Legacy.Keybindings close-tab '<Control><Shift>w'
org.gnome.Terminal.Legacy.Keybindings close-window '<Control><Shift>q'
org.gnome.Terminal.Legacy.Keybindings copy '<Control><Shift>c'
org.gnome.Terminal.Legacy.Keybindings find '<Control><Shift>F'
org.gnome.Terminal.Legacy.Keybindings find-clear '<Control><Shift>J'
org.gnome.Terminal.Legacy.Keybindings find-next '<Control><Shift>G'
org.gnome.Terminal.Legacy.Keybindings find-previous '<Control><Shift>H'
org.gnome.Terminal.Legacy.Keybindings move-tab-left '<Control><Shift>Page_Up'
org.gnome.Terminal.Legacy.Keybindings move-tab-right '<Control><Shift>Page_Down'
org.gnome.Terminal.Legacy.Keybindings new-tab '<Control><Shift>t'
org.gnome.Terminal.Legacy.Keybindings new-window '<Control><Shift>n'
org.gnome.Terminal.Legacy.Keybindings next-tab '<Control>Page_Down'
org.gnome.Terminal.Legacy.Keybindings paste '<Control><Shift>v'
org.gnome.Terminal.Legacy.Keybindings prev-tab '<Control>Page_Up'
org.gnome.Terminal.Legacy.Keybindings zoom-in '<Control>plus'
org.gnome.Terminal.Legacy.Keybindings zoom-normal '<Control>0'
org.gnome.Terminal.Legacy.Keybindings zoom-out '<Control>minus'
org.gnome.desktop.wm.keybindings cycle-panels ['<Control><Alt>Escape']
org.gnome.desktop.wm.keybindings cycle-panels-backward ['<Shift><Control><Alt>Escape']
org.gnome.desktop.wm.keybindings switch-panels ['<Control><Alt>Tab']
org.gnome.desktop.wm.keybindings switch-panels-backward ['<Shift><Control><Alt>Tab']
org.gnome.shell.extensions.screenshot-window-sizer cycle-screenshot-sizes ['<Alt><Control>s']
org.gnome.shell.extensions.screenshot-window-sizer cycle-screenshot-sizes-backward ['<Shift><Alt><Control>s']

org.freedesktop.ibus.panel.emoji'<Control>period'アサインされており, こちらにキーが取られ VScodeの方が効いていないと予想される

ibusのショートカットを変更する

ibus-setupibusの設定画面を起動, emoji タブが下記のようになっていることを確認する

絵文字を直接使うことはまずないし, 使ったこともないのですべて削除した.

最後に

これで VScodeCtrl+. でショートカットが効くようになりました.

merge sort in F#

b.hatena.ne.jp

merge sortを何も見ずに書けるかが気になって書いてみたが特に問題なく書けた. この手の問題は筋トレと同じでいかに普段手を動かして解いている感があるので, leetcodeなどは継続していた方がいいのかなと思った.

open System

let generateNumbers (n: int) : int list =
    let rec generateNumbers' n (r: Random) acc =
        if n = 0 then
            acc
        else
            generateNumbers' (n - 1) r (r.Next(0, 100) :: acc)

    generateNumbers' n (new Random()) []

let merge a b =
    let rec merge' a b acc =
        match a, b with
        | [], [] -> acc |> List.rev
        | h :: t, [] -> merge' t b (h :: acc)
        | [], h :: t -> merge' a t (h :: acc)
        | h1 :: t1, h2 :: t2 ->
            if h1 < h2 then
                merge' t1 b (h1 :: acc)
            else
                merge' a t2 (h2 :: acc)

    merge' a b []

let rec mergeSort (nums: int list) =
    let len = nums.Length

    if len <= 1 then
        nums
    else
        let half = len / 2
        let a = nums |> List.take half |> mergeSort
        let b = nums |> List.skip half |> mergeSort

        merge a b

let nums = generateNumbers 100
let got = mergeSort nums
let expected = List.sort nums

// true
got |> List.zip expected |> List.forall (fun (a, b) -> a = b)

zero length array, flexible array

github.com

たまたま目に入ったこのコードを見ていて, zero length arrayって末尾のメンバ以外でも使えたんだって思って気になるところを調べてみた.

Flexible array

en.wikipedia.org

C99から入った機能. 配列の長さを宣言しないことで利用できる. sizeofはできない. 構造体などの末尾フィールドに置くことができる. 典型的な使い方は以下のような感じ.

struct Data {
   // some fields
   SomeData s[];
};

struct Data *d = malloc(sizeof(Data) + some_arbitrary_size);

ポインタを使ってしまうと構造体とこの末尾のデータがある場所は別の領域になってしまうわけだが, このようにすることで連続するメモリにすべて割り当てることができる.

構造体の末尾以外に置くことはできない

struct Data {
  int a[];
  int b;
};

このようなコードはエラーとなる

a.c:2:7: error: flexible array member not at end of struct
    2 |   int a[];
      |      

Zero length array

gcc.gnu.org

GCC拡張. C90時代から使えたもので, 宣言時に配列サイズを 0とする. Flexible arrayと違い構造体に何個でも宣言でき, GCCでは sizeofを行った場合 0が返るものと規定している.

struct Data {
    int a;
    unsigned char b[0];
    int c[0];
};

このように宣言した場合, bc は同じ先頭アドレスを持ち実質的に union のようになる.

#include <stdio.h>
#include <stdlib.h>

struct Data {
    int a;
    unsigned char b[0];
    int c[0];
};

int main() {
    struct Data *d = malloc(sizeof(int) * 2);
    printf("&d.a=%p\n", &d->a);
    printf("&d.b=%p\n", &d->b);
    printf("&d.c=%p\n", &d->c);

    d->b[0] = 0;
    d->c[0] = 0xdeadbeef;

    printf("d.b[0]=%x\n", d->b[0]);
    printf("d.c[0]=%x\n", d->c[0]);
    return 0;
}

結果は以下の通りで

&d.a=0x559613d242a0
&d.b=0x559613d242a4
&d.c=0x559613d242a4 // cは bと同じアドレス
d.b[0]=ef // cを書き換えると bも変わってしまう
d.c[0]=deadbeef

Flexible array, zero length arrayを使わない場合

今時そんな環境はないと思うが, C99以前かつ GCC拡張が使えない場合は長さ 1の char配列などを置いて代用していたように記憶している.

その他

github.com

上記の変更が含まれる PRにあった non temporalって何ぞやと思ってググると下記の解説があった.

lwn.net

Non-temporal in this context means the data will not be reused soon, so there is no reason to cache it.

とのこと. streamな dataで, 非常に計算速度が重要なものになるとそこまで意識する必要があるんですね. 計算する際キャッシュにうまく載せることは考えたことあったけど不要なものはそもそも載せないという発想がなかった.

いろいろ学びがあった.