Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ripgrep をライブラリとして使う
Search
Linda_pp
November 24, 2021
Technology
760
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ripgrep をライブラリとして使う
Rust LT Online #5 発表資料
https://rust.connpass.com/event/228732/
Linda_pp
November 24, 2021
More Decks by Linda_pp
See All by Linda_pp
actionlint の Linter 設計
rhysd
7
6.6k
port-monolith-to-wasm-for-chrome-extension
rhysd
0
560
Fuzzing Rust Text Editor
rhysd
1
3.1k
Vim compiled to WebAssembly
rhysd
5
2.4k
about-neovim-0.4.0-floating-window
rhysd
3
2.4k
reply.vim
rhysd
0
1.4k
Vim ported to WebAssembly (VimConf 2018)
rhysd
4
3.6k
go-selfupdate-github で ツールを自己アップデートする
rhysd
5
4.6k
小さく始めて育てるコンパイラ
rhysd
13
5.1k
Other Decks in Technology
See All in Technology
AWSとGitHub Actionsの責任境界と 組織で安全に使用する取り組み
nealle
0
210
EMの役割で 変わったこと・変わらなかったこと
sansantech
PRO
0
190
VPCでもFloatingIPを使いたいんだ
y_kotani
1
120
フィジカルAIの知識ゼロの僕でも AIを使えばここまでできた
tatsuya1970
0
200
[RSJ26] Flow as Flow: Modeling Robot Velocity Fields as Probability Velocity Fields
keio_smilab
PRO
0
110
分割40%キーボードにスムーズに入門するには
hoto17296
1
180
Microsoft MVP プログラムを紹介するから目指す人増えてくれ
tsubakimoto_s
0
140
AIレビュー時代に必要なのは、SLOで引く撤退ライン
nobuoooo
0
150
AWSとAzureのマルチクラウド活用における強い味方___AWS_Kiroを使った二刀流スキル作成.pdf
duelist2020jp
0
130
サービス内で複数のOP・ASを連鎖させる(OAuth/OIDC Numa (Immersion) Workshop 2026)
oidfj
PRO
0
350
dbt と Snowflake と tag
kevinrobot34
2
290
指示待ちから変化に応じるClaude Codeへ!~環境からAgentへの帰り道を作る~
gotalab555
7
1.5k
Featured
See All Featured
Facilitating Awesome Meetings
lara
57
7.1k
How STYLIGHT went responsive
nonsquared
100
6.2k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.3k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.8k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
How to train your dragon (web standard)
notwaldorf
97
6.8k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
450
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
Crafting Experiences
bethany
1
270
Raft: Consensus for Rubyists
vanstee
141
7.7k
Six Lessons from altMBA
skipperchong
29
4.5k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
290
Transcript
ripgrep をライブラリとして使う Twitter: @Linda_pp, GitHub: @rhysd Rust LT Online #5
(2021/11/23)
ripgrep とは 有名な grep 互換のコマンドラインツール.SIMD や マルチスレッドを活用し処理を高速化してい る..gitignore を見てくれたり出力に色を付けてくれ たりなど,ユーザフレンドリーな機能も提供.
pcre2 対応を除いて,すべて Rust で実装されてお り,ライブラリとしても使えるようにうまくモジュール化 されている.
活用例 hgrep (Human-friendly GREP) というツールでファイ ル検索に利用. 検索結果を良い感じにスニペットにまとめて構文ハイ ライトして出力するコマンドラインツール. - リポジトリ:
https://github.com/rhysd/hgrep - 紹介記事: https://rhysd.hatenablog.com/entry/2021/11/2 3/211530
TL;DR https://github.com/rhysd/hgrep/bl ob/main/src/ripgrep.rs ↑ここにすべての実装が入っている grep 出力 パーサ grep 実装 chunk
の 計算処理 Printer Bat Printer Syntect Printer ↓ ここの話 --no-ignore --ignore-case --smart-case --glob GLOB... --glob-case-insensitive --fixed-strings --word-regexp --follow --multiline --multiline-dotall --crlf --mmap --max-count NUM --max-depth NUM --max-filesize NUM+SUFFIX? --line-regexp --invert-match --pcre2 --type TYPE --type-not TYPE --type-list --one-file-system --no-unicode --regex-size-limit --dfa-size-limit
ripgrep のモジュール構造 ripgrep cli core ignore globpath matcher pcre2 regex
memmem memchr printer searcher ripgrep 用 CLI パーサ (grep-cli) 本体(main 関数はここ) ディレクトリを辿ってファイルパスを列挙 **/*.rs のような glob のファイルパス列挙 ファイルに対するマッチ処理 (grep-matcher) pcre2 の Rust binding 有名な regex crate libc の memmem の SIMD 実装 libc の memchr の SIMD 実装 ファイルの検索処理 (grep-searcher) マッチ結果の表示処理 (grep-printer)
ripgrep のモジュール構造 ripgrep cli core ignore globpath matcher pcre2 regex
memmem memchr printer searcher ripgrep 用 CLI パーサ (grep-cli) 本体(main 関数はここ) ディレクトリを辿ってファイルパスを列挙 **/*.rs のような glob のファイルパス列挙 ファイルに対するマッチ処理 (grep-matcher) pcre2 の Rust binding 有名な regex crate libc の memmem の SIMD 実装 libc の memchr の SIMD 実装 ファイルの検索処理 (grep-searcher) マッチ結果の表示処理 (grep-printer)
サンプルコード https://github.com/rhysd/misc/tree/master/rust/chibigrep - ripgrep のうち再帰的にファイルを検索してマッチ結果を 取得する処理部分だけを使う - 特定のテキストを含んだファイルを探してくるユースケー スを想定 -
ignore, grep-matcher, grep-searcher の3つの crate を 使う
ディレクトリの walker を生成 use ignore::{WalkBuilder, WalkState}; // Path を再帰的に辿る walker
を生成.今回は WalkParallel でマルチスレッドでパスを 辿る // スレッドプールは自動で生成される(スレッド数は指定もできるが,デフォルトで良い感じに決 めてくれる) let mut builder = WalkBuilder::new(path); for path in rest { builder.add(path); } builder .hidden(false) // 隠しファイルを検索 .ignore(true) // ignore されたファイルを無視 .parents(true); // 親ディレクトリを辿って .gitignore を探す let walker = builder.build_parallel();
ディレクトリをマルチスレッドで再帰的に辿る let (tx, rx) = mpsc::channel(); // walker.run はマルチスレッドで呼ばれるので値の受け渡しを channel
でやる walker.run(|| { // 初期化関数.ここはスレッドプールのスレッドごとに呼ばれる let tx = tx.clone(); Box::new(move |result| match result { // この内側のコールバックはファイルパスごとに呼ばれる Ok(entry) if entry.file_type().map(|t| t.is_file()).unwrap_or(false) => { // `entry` は `ignore::DirEntry` grep_file(pat, entry.into_path(), &tx); // ファイルの時.ファイル内を検索 ignore::WalkState::Continue // 検索を続ける } Ok(_) => ignore::WalkState::Continue, // ディレクトリの時.検索を続ける Err(err) => { tx.send(Err(format!("{}", err))).unwrap(); ignore::WalkState::Quit // 検索を中止する } }) });
マッチ結果を受け取る Sink を実装 use grep_searcher::{Sink, SinkMatch}; struct SearchSink<'a> { tx:
&'a Sender<Result<Match, MyError>>, path: &'a Path, } // 結果を集めるためのコールバックを Sink で実装.マッチ箇所ごとに `matched` が呼ばれる impl<'a> Sink for SearchSink<'a> { type Error = io::Error; // `SinkMatch` にマッチ情報が入っている fn matched(&mut self, _searcher: &Searcher, mat: &SinkMatch<'_>) -> Result<bool, Self::Error> { let m = Match { path: self.path.to_owned(), lnum: mat.line_number().unwrap_or(0), line: mat.bytes().to_vec(), }; self.tx.send(Ok(m)).unwrap(); // マッチ結果を返す Ok(true) } }
マッチ処理を行う matcher を生成 use grep_regex::RegexMatcherBuilder; let mut builder = RegexMatcherBuilder::new();
builder .case_smart(true) // smart case を有効に .unicode(true); // unicode 対応 // Matcher を生成.今回は regex crate を使った RegexMatcher を使う // これ以外にも pcre2 を使ったものもある let matcher = match builder.build(pat) { Ok(m) => m, Err(err) => { tx.send(Err(format!("{}", err))).unwrap(); return; } };
searcher を生成してファイル内を検索 use grep_searcher::{BinaryDetection, MmapChoice, SearcherBuilder}; // Searcher を生成 let
mut builder = SearcherBuilder::new(); builder .binary_detection(BinaryDetection::quit(0)) // バイナリファイルだと判明したら検索をやめる .line_number(true) .memory_map(unsafe { MmapChoice::auto() }); // mmap を有効にする let mut searcher = builder.build(); // ここでファイルを検索.マッチごとに sink の matched メソッドが呼ばれる let mut sink = SearchSink { tx, path: &path }; if let Err(err) = searcher.search_path(&matcher, &path, &mut sink) { tx.send(Err(format!("{}", err))).unwrap(); }
まとめ • ripgrep は個々の機能ごと複数の crate に分割して実装されている ◦ ファイルパス検索(ignore) ◦ マッチ処理(grep-regex,
grep-matcher) ◦ ファイル検索(grep-searcher) ◦ 結果表示(grep-printer) • 「特定のパターンのテキストを含んだファイルを探してくる」という処理に対してはか なり汎用的・簡単に使うことができ,実行効率も良い(マルチスレッド活用・SIMD に よる検索実装)