Compare commits
12
Commits
main
..
d4059d4869
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4059d4869 | ||
|
|
3785af9bb4 | ||
|
|
94e5d7bfa5 | ||
|
|
499de6df04 | ||
|
|
79129a9d7d | ||
|
|
920364cfa7 | ||
|
|
ef1a7a54b4 | ||
|
|
8e4a8f6f09 | ||
|
|
996c4d0143 | ||
|
|
1c7d0a6018 | ||
|
|
cf12bcb001 | ||
|
|
a2a440015d |
@@ -7,19 +7,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- run: apt update
|
- run: apt update
|
||||||
- run: apt install build-essential valgrind make curl wget libcurl4-openssl-dev libxml2-dev binutils libc6-dev libgcc-s1 libstdc++6 zlib1g-dev -y
|
- run: apt install build-essential valgrind make -y
|
||||||
- run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain nightly -y
|
|
||||||
- name: Install Swift
|
|
||||||
run: |
|
|
||||||
cd /opt
|
|
||||||
wget -q https://download.swift.org/swift-5.9.2-release/ubuntu2204/swift-5.9.2-RELEASE/swift-5.9.2-RELEASE-ubuntu22.04.tar.gz
|
|
||||||
tar xzf swift-5.9.2-RELEASE-ubuntu22.04.tar.gz
|
|
||||||
ln -s /opt/swift-5.9.2-RELEASE-ubuntu22.04/usr/bin/swift /usr/local/bin/swift
|
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: List files in the repository
|
- name: List files in the repository
|
||||||
run: |
|
run: |
|
||||||
ls ${{ gitea.workspace }}
|
ls ${{ gitea.workspace }}
|
||||||
- run: export PATH=$HOME/.cargo/bin:/opt/swift-5.9.2-RELEASE-ubuntu22.04/usr/bin:$PATH && make build_all
|
- run: make valgrind
|
||||||
- run: export PATH=$HOME/.cargo/bin:/opt/swift-5.9.2-RELEASE-ubuntu22.04/usr/bin:$PATH && make benchmark
|
|
||||||
- run: make publish
|
|
||||||
|
|||||||
+2
-16
@@ -1,17 +1,3 @@
|
|||||||
.r_history
|
|
||||||
.history
|
.history
|
||||||
.vscode/
|
.vscode
|
||||||
publish/
|
publish
|
||||||
books/
|
|
||||||
__pycache__/
|
|
||||||
target/
|
|
||||||
./isspam.py
|
|
||||||
/isspam
|
|
||||||
/risspam
|
|
||||||
/jisspam
|
|
||||||
/sisspam
|
|
||||||
/isspam_cpp
|
|
||||||
/borded_cpp_exec
|
|
||||||
/isspam.py
|
|
||||||
swift_isspam/.build/
|
|
||||||
.build-trigger-2014-12-02 15:26
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
#![feature(let_chains)]\n\nuse std::{env, fs};\n\nfn clean_content(content: &str) -> String {\n\tlet alloed_ichars = \"01234567891abcdefghijklmnopqrstuvwxyz \\n.,!?\";\n\t\n\tlet clean_content = content.chars()\n\t\t.filter(|&c| alloed_ichars.contains(c))\n\t\t.collect::<String>();\n\t\n\tclean_content\n}\n\nfn get_sentences(content: &str) -> Vec<&str> {\n\tlet mut sentences = content.split('.')\n\t\t.map(|s| s.trim_start()) // Remove leading whitespace\n\t\t.collect::<Vec<_>>();\n\t\n\t// Remove last \"sentence\" if didn't end with a dot\n\tif let Some(last) = sentences.last() && !last.ends_with('.') {\n\t\tsentences.pop();\n\t}\n\t\n\tsentences\n}\n\nfn get_words(sentences: &str) -> impl Iterator<Item = &str> + Clone {\n\tsentences.split_whitespace()\n}\n\nfn is_fully_capitalized_word(word: &str) -> bool {\n\tword.chars()\n\t\t.all(|c| !c.is_ascii_alphanumeric() || c.is_ascii_uppercase())\n}\n\nfn get_capitalized_words(content: &str) -> Vec<&str> {\n\tlet sentences = get_sentences(content);\n\tlet mut cap_words = vec![];\n\t\n\tfor sentence in sentences {\n\t\t// Always skip the first word since sentences start with\n\t\tfor word in get_words(sentence).skip(1) {\n\t\t\tif is_fully_capitalized_word(word) {\n\t\t\t\tcap_words.push(word);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tcap_words\n}\n\nfn get_numbers(content: &str) -> Vec<String> {\n\tlet clean = clean_content(content);\n\t\n\tclean.split(|c: char| c.is_ascii_digit())\n\t\t.map(|n| n.to_string())\n\t\t.collect()\n}\n\nfn get_forbidden_words(content: &str) -> Vec<&str> {\n\tfn check_forbidden(w: &str) -> bool {\n\t\tFORBIDDEN_WORDS.iter()\n\t\t\t.find(|fw| str::eq_ignore_ascii_case(w, fw))\n\t\t\t.is_some()\n\t}\n\t\n\tget_words(content)\n\t\t.filter(|w| check_forbidden(w))\n\t\t.collect()\n}\n\nfn analyze(data: &str) {\n\tlet clean_data = clean_content(data);\n\tdrop(clean_data); // You aren't actually using clean_data :O\n\t\n\t// All capitalized words\n\tlet cap_words = get_capitalized_words(data);\n\tprintln!(\"All capitalized words: {}\", cap_words.len());\n\t\n\t// All sentences\n\tlet sentences = get_sentences(data);\n\tprintln!(\"Sentences: {}\", sentences.len());\n\t\n\t// All words\n\tlet words = get_words(data);\n\tprintln!(\"Words: {}\", words.clone().count());\n\t\n\t// Numbers\n\tlet numbers = get_numbers(data);\n\tprintln!(\"Numbers: {}\", numbers.len());\n\t\n\t// Forbidden words\n\tlet fw = get_forbidden_words(data);\n\tprintln!(\"Forbidden words: {}\", fw.len());\n\t\n\tlet word_count_per_sentence = words.count() / sentences.len();\n\tprintln!(\"Word count per sentence: {}\", word_count_per_sentence);\n}\n\nfn main() {\n // Read in files from args\n for arg in env::args().skip(1) { // skip program arg\n \tlet Ok(text) = fs::read_to_string(&arg) else {\n \t\teprintln!(\"{arg} isn't a valid file or couldn't be read\");\n \t\tcontinue;\n \t};\n \t\n \tanalyze(&text);\n }\n \n//\tanalyze(&SPAM1);\n}\n\nstatic FORBIDDEN_WORDS: &'static [&'static str] = &[\n \"recovery\", \"techie\", \"http\", \"https\", \"digital\", \"hack\", \"::\", \"//\", \"com\",\n \"@\", \"crypto\", \"bitcoin\", \"wallet\", \"hacker\", \"welcome\", \"whatsapp\", \"email\", \"cryptocurrency\",\n \"stolen\", \"freeze\", \"quick\", \"crucial\", \"tracing\", \"scammers\", \"expers\", \"hire\", \"century\",\n \"transaction\", \"essential\", \"managing\", \"contact\", \"contacting\", \"understanding\", \"assets\", \"funds\"\n];\n
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#!+[feature(let_chains)]\n\n\nfn clean_content(content: &str) -> String {\n\tlet alloed_ichars = \"01234567891abcdefghijklmnopqrstuvwxyz \\n.,!?\";\n\t\n\tlet clean_content = content.chars()\n\t\t.filter(|&c| alloed_ichars.contains(c))\n\t\t.collect::<String>();\n\t\n\tclean_content\n}\n\nfn get_sentences(content: &str) -> Vec<&str> {\n\tlet mut sentences = content.split('.')\n\t\t.map(|s| s.trim_start()) // Remove leading whitespace\n\t\t.collect::<Vec<_>>();\n\t\n\t// Remove last \"sentence\" if didn't end with a dot\n\tif let Some(last) = sentences.last() && !last.ends_with('.') {\n\t\tsentences.pop();\n\t}\n\t\n\tsentences\n}\n\nfn get_words(sentences: &str) -> impl Iterator<Item = &str> + Clone {\n\tsentences.split_whitespace()\n}\n\nfn is_fully_capitalized_word(word: &str) -> bool {\n\tword.chars()\n\t\t.all(|c| !c.is_ascii_alphanumeric() || c.is_ascii_uppercase())\n}\n\nfn get_capitalized_words(content: &str) -> Vec<&str> {\n\tlet sentences = get_sentences(content);\n\tlet mut cap_words = vec![];\n\t\n\tfor sentence in sentences {\n\t\t// Always skip the first word since sentences start with\n\t\tfor word in get_words(sentence).skip(1) {\n\t\t\tif is_fully_capitalized_word(word) {\n\t\t\t\tcap_words.push(word);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tcap_words\n}\n\nfn get_numbers(content: &str) -> Vec<String> {\n\tlet clean = clean_content(content);\n\t\n\tclean.split(|c: char| c.is_ascii_digit())\n\t\t.map(|n| n.to_string())\n\t\t.collect()\n}\n\nfn get_forbidden_words(content: &str) -> Vec<&str> {\n\tfn check_forbidden(w: &str) -> bool {\n\t\tFORBIDDEN_WORDS.iter()\n\t\t\t.find(|fw| str::eq_ignore_ascii_case(w, fw))\n\t\t\t.is_some()\n\t}\n\t\n\tget_words(content)\n\t\t.filter(|w| check_forbidden(w))\n\t\t.collect()\n}\n\nfn analyze(data: &str) {\n\tlet clean_data = clean_content(data);\n\tdrop(clean_data); // You aren't actually using clean_data :O\n\t\n\t// All capitalized words\n\tlet cap_words = get_capitalized_words(data);\n\tprintln!(\"All capitalized words: {}\", cap_words.len());\n\t\n\t// All sentences\n\tlet sentences = get_sentences(data);\n\tprintln!(\"Sentences: {}\", sentences.len());\n\t\n\t// All words\n\tlet words = get_words(data);\n\tprintln!(\"Words: {}\", words.clone().count());\n\t\n\t// Numbers\n\tlet numbers = get_numbers(data);\n\tprintln!(\"Numbers: {}\", numbers.len());\n\t\n\t// Forbidden words\n\tlet fw = get_forbidden_words(data);\n\tprintln!(\"Forbidden words: {}\", fw.len());\n\t\n\tlet word_count_per_sentence = words.count() / sentences.len();\n\tprintln!(\"Word count per sentence: {}\", word_count_per_sentence);\n}\n\nfn main() {\n//\t// Read in files from args\n//\tfor arg in env::args() {\n//\t\tlet Ok(text) = fs::read_to_string(arg) else {\n//\t\t\teprintln!(\"{arg} isn't a valid file or couldn't be read\");\n//\t\t\tcontinue;\n//\t\t};\n//\t\t\n//\t\t
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#![feature(let_chains)]\\n\\nuse rayon::prelude::*;\\n//use rayon::prelude::*;\\nuse std::{env, fs};\\n\\nfn clean_content(content: &str) -> String {\\n\\tlet alloed_ichars = \\\"01234567891abcdefghijklmnopqrstuvwxyz \\\\n.,!?\\\";\\n\\t\\n\\tlet clean_content = content.chars()\\n\\t\\t.filter(|&c| alloed_ichars.contains(c))\\n\\t\\t.collect::<String>();\\n\\t\\n\\tclean_content\\n}\\n\\nfn get_sentences(content: &str) -> usize {\\n\\tlet sentences = content.split('.')\\n\\t\\t.map(|s| s.trim_start()) // Remove leading whitespace\\n\\t\\t.count();\\n\\t\\n//\\t// Remove last \\\"sentence\\\" if didn't end with a dot\\n//\\tif let Some(last) = sentences.last() && !last.ends_with('.') {\\n//\\t\\tsentences.pop();\\n//\\t}\\n\\t\\n\\tsentences\\n}\\n\\nfn get_words(content: &str, words: &mut usize, caps: &mut usize, fw: &mut usize) {\\n\\tfn check_forbidden(w: &str) -> bool {\\n\\t\\tFORBIDDEN_WORDS.iter()\\n\\t\\t\\t.find(|fw| str::eq_ignore_ascii_case(w, fw))\\n\\t\\t\\t.is_some()\\n\\t}\\n\\t\\n\\tfor word in content.split_whitespace() {\\n\\t\\t*words += 1;\\n\\t\\t\\n\\t\\tif is_fully_capitalized_word(word) {\\n\\t\\t\\t*caps += 1;\\n\\t\\t}\\n\\t\\tif check_forbidden(word) {\\n\\t\\t\\t*fw += 1;\\n\\t\\t}\\n\\t}\\n}\\n\\nfn is_fully_capitalized_word(word: &str) -> bool {\\n\\tword.chars()\\n\\t\\t.all(|c| !c.is_ascii_alphanumeric() || c.is_ascii_uppercase())\\n}\\n\\nfn get_numbers(clean_content: &str) -> usize {\\n\\tclean_content.split(|c: char| !c.is_ascii_digit())\\n\\t\\t.count()\\n}\\n\\nfn analyze(data: &str) {\\n\\tlet clean_data = clean_content(data);\\n//\\tdrop(clean_data); // You aren't actually using clean_data :O\\n\\t\\n\\t// All capitalized words\\n\\tlet mut words = 0;\\n\\tlet mut fw = 0;\\n\\tlet mut cap_words = 0;\\n\\tget_words(&clean_data, &mut words, &mut fw, &mut cap_words);\\n\\t\\n\\tprintln!(\\\"All capitalized words: {}\\\", cap_words);\\n\\t\\n\\t// All sentences\\n\\tlet sentences = get_sentences(data);\\n\\tprintln!(\\\"Sentences: {}\\\", sentences);\\n\\t\\n\\t// All words\\n\\tprintln!(\\\"Words: {}\\\", words);\\n\\t\\n\\t// Numbers\\n\\tlet numbers = get_numbers(&clean_data);\\n\\tprintln!(\\\"Numbers: {}\\\", numbers);\\n\\t\\n\\t// Forbidden words\\n\\tprintln!(\\\"Forbidden words: {}\\\", fw);\\n\\t\\n\\tif sentences > 0 {\\n\\t\\tlet word_count_per_sentence = words / sentences;\\n\\t\\tprintln!(\\\"Word count per sentence: {}\\\", word_count_per_sentence);\\n\\t}\\n}\\n\\nfn main() {\\n // Read in files from args\\n\\tlet mut files = Vec::with_capacity(env::args().len());\\n\\tlet mut do_parallel = false;\\n\\t\\n\\tfor arg in env::args().skip(1) { // skip program arg\\n\\t\\tif arg == \\\"-p\\\" {\\n\\t\\t\\tdo_parallel = true;\\n\\t\\t} else {\\n\\t\\t\\tfiles.push(arg);\\n\\t\\t}\\n\\t}\\n\\t\\n\\t// Do the work\\n\\tlet work = |file| {\\n\\t let Ok(text) = fs::read_to_string(&file) else {\\n\\t\\t\\teprintln!(\\\"{file} isn't a valid file or couldn't be read\\\");\\n\\t\\t\\treturn;\\n\\t };\\n\\t \\tanalyze(&text);\\n\\t};\\n\\t\\n\\tif !do_parallel {\\n\\t\\tfiles.iter().for_each(work);\\n\\t} else {\\n\\t\\tfiles.par_iter().for_each(work)\\n\\t}\\n}\\n\\nstatic FORBIDDEN_WORDS: &'static [&'static str] = &[\\n \\\"recovery\\\", \\\"techie\\\", \\\"http\\\", \\\"https\\\", \\\"digital\\\", \\\"hack\\\", \\\"::\\\", \\\"//\\\", \\\"com\\\",\\n \\\"@\\\", \\\"crypto\\\", \\\"bitcoin\\\", \\\"wallet\\\", \\\"hacker\\\", \\\"welcome\\\", \\\"whatsapp\\\", \\\"email\\\", \\\"cryptocurrency\\\",\\n \\\"stolen\\\", \\\"freeze\\\", \\\"quick\\\", \\\"crucial\\\", \\\"tracing\\\", \\\"scammers\\\", \\\"expers\\\", \\\"hire\\\", \\\"century\\\",\\n \\\"transaction\\\", \\\"essential\\\", \\\"managing\\\", \\\"contact\\\", \\\"contacting\\\", \\\"understanding\\\", \\\"assets\\\", \\\"funds\\\"\\n];\\n
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#![feature(let_chains)]\\n\\nuse rayon::prelude::*;\\n//use rayon::prelude::*;\\nuse std::{env, fs};\\n\\nfn clean_content(content: &str) -> String {\\n\\tlet alloed_ichars = \\\"01234567891abcdefghijklmnopqrstuvwxyz \\\\n.,!?\\\";\\n\\t\\n\\tlet clean_content = content.chars()\\n\\t\\t.filter(|&c| alloed_ichars.contains(c))\\n\\t\\t.collect::<String>();\\n\\t\\n\\tclean_content\\n}\\n\\nfn get_sentences(content: &str) -> usize {\\n\\tlet sentences = content.split('.')\\n\\t\\t.map(|s| s.trim_start()) // Remove leading whitespace\\n\\t\\t.count();\\n\\t\\n//\\t// Remove last \\\"sentence\\\" if didn't end with a dot\\n//\\tif let Some(last) = sentences.last() && !last.ends_with('.') {\\n//\\t\\tsentences.pop();\\n//\\t}\\n\\t\\n\\tsentences\\n}\\n\\nfn get_words(content: &str, words: &mut usize, caps: &mut usize, fw: &mut usize) {\\n\\tfn check_forbidden(w: &str) -> bool {\\n\\t\\tFORBIDDEN_WORDS.iter()\\n\\t\\t\\t.find(|fw| str::eq_ignore_ascii_case(w, fw))\\n\\t\\t\\t.is_some()\\n\\t}\\n\\t\\n\\tfor word in content.split_whitespace() {\\n\\t\\t*words += 1;\\n\\t\\t\\n\\t\\tif is_fully_capitalized_word(word) {\\n\\t\\t\\t*caps += 1;\\n\\t\\t}\\n\\t\\tif check_forbidden(word) {\\n\\t\\t\\t*fw += 1;\\n\\t\\t}\\n\\t}\\n}\\n\\nfn is_fully_capitalized_word(word: &str) -> bool {\\n\\tword.chars()\\n\\t\\t.all(|c| !c.is_ascii_alphanumeric() || c.is_ascii_uppercase())\\n}\\n\\nfn get_numbers(clean_content: &str) -> usize {\\n\\tclean_content.split(|c: char| !c.is_ascii
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[build]
|
|
||||||
rustflags = [
|
|
||||||
"-Ztls-model=initial-exec",
|
|
||||||
"-Ctarget-cpu=native"
|
|
||||||
]
|
|
||||||
|
|
||||||
#[unstable]
|
|
||||||
#build-std = ["compiler_builtins", "alloc", "std", "panic_abort"] # choose only what you need
|
|
||||||
#build-std-features = ["compiler-builtins-mem"]
|
|
||||||
Generated
-61
@@ -1,61 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "risspam"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"rayon",
|
|
||||||
]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "risspam"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = "thin"
|
|
||||||
panic = "abort"
|
|
||||||
codegen-units = 1
|
|
||||||
debug = "line-tables-only"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rayon = "1.10.0"
|
|
||||||
#mimalloc = "0.1.48"
|
|
||||||
#io-uring = "0.7"
|
|
||||||
#memmap2 = "0.9.8"
|
|
||||||
#core_affinity = "0.8.3"
|
|
||||||
#libc = "0.2.176"
|
|
||||||
#glommio = "0.9.0"
|
|
||||||
#monoio = "0.2.4"
|
|
||||||
#phf = { version = "0.13.1", features = ["macros"] }
|
|
||||||
@@ -1,907 +0,0 @@
|
|||||||
|
|
||||||
pub static FULL_BOOK_PATHS: &[&'static str] = &[
|
|
||||||
"books/0192806807.pdf - Unknown.txt",
|
|
||||||
"books/0_Deep Learning Cookbook - Practical Recipes to Get Started Quickly.txt",
|
|
||||||
"books/0_Deep Learning for Search.txt",
|
|
||||||
"books/0_Deep Learning with Python.txt",
|
|
||||||
"books/10Algorithms-08.txt",
|
|
||||||
"books/1407.7502v3.txt",
|
|
||||||
"books/1491912766_Advanced.txt",
|
|
||||||
"books/18374.txt",
|
|
||||||
"books/2014-data-science-salary-survey.txt",
|
|
||||||
"books/21 Recipes for Mining Twitter.txt",
|
|
||||||
"books/240415.txt",
|
|
||||||
"books/3dprinting.txt",
|
|
||||||
"books/9780077418182.pdf - W. Schiff.txt",
|
|
||||||
"books/A Developer’s Guide to the Semantic Web.txt",
|
|
||||||
"books/Advanced Analytics with Spark - Patterns for Learning from Data at Scale.txt",
|
|
||||||
"books/Advanced Analytics with Spark - Sandy Ryza, Uri Laserson, Sean Owen.txt",
|
|
||||||
"books/AdvancedBashScripting.txt",
|
|
||||||
"books/advanced-microservices.txt",
|
|
||||||
"books/Advanced Techniques in Web Intelligence – Part II.txt",
|
|
||||||
"books/Advanced Techniques in Web Intelligence – Part I.txt",
|
|
||||||
"books/Agile Business Intelligence.txt",
|
|
||||||
"books/Agile Data Science.txt",
|
|
||||||
"books/Agile Estimating and Planning.txt",
|
|
||||||
"books/Agile for Everybody - Creating Fast, Flexible, and Customer First Organizations.txt",
|
|
||||||
"books/Agile Methods - Large-Scale Development, Refactoring, Testing, and Estimation.txt",
|
|
||||||
"books/Agile Retrospectives - Making Good Teams Great.txt",
|
|
||||||
"books/Agile_Software_Development.txt",
|
|
||||||
"books/Agile Testing - A Practical Guide for Testers and Agile Teams.txt",
|
|
||||||
"books/Algorithmic Graph Theory and Sage.txt",
|
|
||||||
"books/Algorithms for Interviews.txt",
|
|
||||||
"books/algoritmos-programacion-Python.txt",
|
|
||||||
"books/a-little-book-of-r-for-time-series.txt",
|
|
||||||
"books/Amazon Web Services in Action.txt",
|
|
||||||
"books/[Andreas_M._Antonopoulos]_Mastering_Bitcoin_Unloc(BookZZ.org).txt",
|
|
||||||
"books/android9developmentcookbook.txt",
|
|
||||||
"books/AndroidForensics.txt",
|
|
||||||
"books/androidprogrammingforbeginners.txt",
|
|
||||||
"books/AndroidProgrammingPushingTheLimits.txt",
|
|
||||||
"books/AndroidSensorProgramming.txt",
|
|
||||||
"books/AndroidUIDesign.txt",
|
|
||||||
"books/AngualrJS Fundamentals.txt",
|
|
||||||
"books/angular-2-test-driven-development-2nd.txt",
|
|
||||||
"books/Angular2.txt",
|
|
||||||
"books/angular6forenterprise-readywebapplications.txt",
|
|
||||||
"books/Angular in Action.txt",
|
|
||||||
"books/AngularJS by Example.txt",
|
|
||||||
"books/AngularJS by Example - Unknown.txt",
|
|
||||||
"books/AngularJsNoviceToNinja.txt",
|
|
||||||
"books/AngularJS.txt",
|
|
||||||
"books/Angular.txt",
|
|
||||||
"books/angular_upandrunning.txt",
|
|
||||||
"books/AnIntroductionToGCC.txt",
|
|
||||||
"books/AnIntroductionToGNUMakeTool.txt",
|
|
||||||
"books/An Introduction to Information Retreival.txt",
|
|
||||||
"books/An Introduction to Machine Learning Interpretability.txt",
|
|
||||||
"books/antitextbookGo.txt",
|
|
||||||
"books/Anything You Want - 40 Lessons for a New Kind of Entrepreneur.txt",
|
|
||||||
"books/Apache Kafka Cookbook.txt",
|
|
||||||
"books/Apache Mesos Cookbook.txt",
|
|
||||||
"books/Apache Sqoop Cookbook.txt",
|
|
||||||
"books/ApacheTomcatCookbook.txt",
|
|
||||||
"books/API Design Cookbook.txt",
|
|
||||||
"books/api-driven-devops.txt",
|
|
||||||
"books/APIs A Strategy Guide.txt",
|
|
||||||
"books/Applied Text Analysis with Python - Enabling Language Aware Data Products with Machine Learning.txt",
|
|
||||||
"books/AprendiendoJavaScript(spanish).txt",
|
|
||||||
"books/Architecting Modern Data Platforms - A Guide To Enterprise Hadoop At Scale.txt",
|
|
||||||
"books/architectingmodernjavaeeapplications.txt",
|
|
||||||
"books/Arduino_Succinctly.txt",
|
|
||||||
"books/artificialintelligenceinthe21stcentury.txt",
|
|
||||||
"books/Art of Agile Development.txt",
|
|
||||||
"books/artofdatascience.txt",
|
|
||||||
"books/aspectos_avanzados_en_seguridad_en_redes_modulos.txt",
|
|
||||||
"books/aspnetcore2andangular5.txt",
|
|
||||||
"books/ASPNetCore.txt",
|
|
||||||
"books/aspnetmvc4_Succinctly.txt",
|
|
||||||
"books/ASP.NET_MVC_Succinctly.txt",
|
|
||||||
"books/Atomic Habits - An Easy & Proven Way to Build Good Habits & Break Bad Ones.txt",
|
|
||||||
"books/autocad2019beginningandintermediate.txt",
|
|
||||||
"books/autocad20203dmodeling.txt",
|
|
||||||
"books/autodeskrevit2020architecture.txt",
|
|
||||||
"books/Automate the Boring Stuff with Python.txt",
|
|
||||||
"books/A Workflow Approach to Stream Processing.txt",
|
|
||||||
"books/Bad Data Handbook - Cleaning Up The Data So You Can Get Back To Work.txt",
|
|
||||||
"books/bashcookbook.txt",
|
|
||||||
"books/Bash Guide for Beginners.txt",
|
|
||||||
"books/BasicsProgrammableLogicControllerPrinciples.txt",
|
|
||||||
"books/Bayesian_computation_with_R-libre.txt",
|
|
||||||
"books/Bayesian Networks and Influence Diagrams A Guide to Construction and Analysis.txt",
|
|
||||||
"books/Bayesian Reasoning and Machine Learning .txt",
|
|
||||||
"books/bdd-in-action.txt",
|
|
||||||
"books/BDD.txt",
|
|
||||||
"books/Beautiful Code.txt",
|
|
||||||
"books/Beautiful_Code.txt",
|
|
||||||
"books/Beautiful Data.txt",
|
|
||||||
"books/Beautiful Visualization.txt",
|
|
||||||
"books/become-ninja-angular2.txt",
|
|
||||||
"books/Beginning Amazon Web Services with Node.js.txt",
|
|
||||||
"books/BeginningAndroidGames.txt",
|
|
||||||
"books/BeginningJSON.txt",
|
|
||||||
"books/Big_Data_Analytics_with_R.txt",
|
|
||||||
"books/Big Data Analytics with Spark - A Practitioner's Guide to Using Spark for Large Scale Data Analysis.txt",
|
|
||||||
"books/Big Data, Data Mining and Machine Learning.txt",
|
|
||||||
"books/Big Data For Dummies.txt",
|
|
||||||
"books/Big Data Glossary.txt",
|
|
||||||
"books/Blockchain.txt",
|
|
||||||
"books/bookL.txt",
|
|
||||||
"books/book-no-solutions-aug-21-2014.txt",
|
|
||||||
"books/book.txt",
|
|
||||||
"books/BootstrapCookbook.txt",
|
|
||||||
"books/build-apis-you-wont-hate.txt",
|
|
||||||
"books/Building Adaptable Software with Microservices.txt",
|
|
||||||
"books/Building Evolutionary Architectures.txt",
|
|
||||||
"books/Building Hypermedia APIs with HTML5 and Node.txt",
|
|
||||||
"books/Building Hypermedia APIs with HTML5 and No - Mike Amundsen.txt",
|
|
||||||
"books/Building Machine Learning Projects with TensorFlow.txt",
|
|
||||||
"books/Building Machine Learning Systems with Python.txt",
|
|
||||||
"books/building-microservices-designing-fine-grained-systems.txt",
|
|
||||||
"books/Building Microservices.txt",
|
|
||||||
"books/buildingrestfulpythonwebservices - Unknown.txt",
|
|
||||||
"books/Building-web-apps-with-Node.js.txt",
|
|
||||||
"books/BuildingWebAppsWithNode.js.txt",
|
|
||||||
"books/Business Adventures - Twelve Classic Tales from the World of Wall Street.txt",
|
|
||||||
"books/Business Intelligence Data Mining and Optimization for Decision Making.txt",
|
|
||||||
"books/business-models-for-data-economy.txt",
|
|
||||||
"books/Can I Freeze It_ _ How to Use the Most Ver - Susie Theodorou.txt",
|
|
||||||
"books/Category Theory for Computer Science.txt",
|
|
||||||
"books/Category Theory for Computer Science - Unknown.txt",
|
|
||||||
"books/C++_CreatingGamesStepByStepGUIDE.txt",
|
|
||||||
"books/Chapter-13-Association-Rules.txt",
|
|
||||||
"books/Chapter-14-Cluster-Analysis.txt",
|
|
||||||
"books/Chapter-16-Regression-Based-Forecasting.txt",
|
|
||||||
"books/Chapter-1-Introduction.txt",
|
|
||||||
"books/Chapter-2-Overview-of-the-Data-Mining-Process.txt",
|
|
||||||
"books/Chapter-3-Data-Visualization.txt",
|
|
||||||
"books/Chapter 4_Dimension Reduction (Data Mining - Nitin R. Patel.txt",
|
|
||||||
"books/Chapter-4-Dimension-Reduction.txt",
|
|
||||||
"books/Chapter-5-Evaluating-Classification-and-Predictive-Performance.txt",
|
|
||||||
"books/Chapter-6-Multiple-Linear-Regression.txt",
|
|
||||||
"books/Chapter-7-k-Nearest-Neighbors--k-NN-.txt",
|
|
||||||
"books/Chapter-8-Naive-Bayes.txt",
|
|
||||||
"books/Chapter-9-Classification-and-Regression-Trees.txt",
|
|
||||||
"books/Christian Rudder-Dataclysm_ who we are (when we think no one's looking)-Crown (2014).txt",
|
|
||||||
"books/ciml-v0_9-all.txt",
|
|
||||||
"books/Classic Computer Science Problems in Python.txt",
|
|
||||||
"books/classicgamedesign.txt",
|
|
||||||
"books/classicshellscripting.txt",
|
|
||||||
"books/Clean Code - A Handbook of Agile Software Craftsmanship.txt",
|
|
||||||
"books/clean-coder-conduct-professional-programmers.txt",
|
|
||||||
"books/Clean Code.txt",
|
|
||||||
"books/cloudcomputingbasics_aselfteachingintroduction.txt",
|
|
||||||
"books/Cloud Native DevOps with Kubernetes.txt",
|
|
||||||
"books/Cloud Native Patterns - Designing change tolerant software.txt",
|
|
||||||
"books/Code Complete - A Practical Handbook of Software Construction.txt",
|
|
||||||
"books/Code Complete.txt",
|
|
||||||
"books/Collaborative filtering.txt",
|
|
||||||
"books/Collective Intelligence in Action.txt",
|
|
||||||
"books/Collective Intelligence.txt",
|
|
||||||
"books/Competing Against Luck - The Story of Innovation and Customer Choice.txt",
|
|
||||||
"books/Compilers-Principles-TechniquesAndTools2ndEdition.txt",
|
|
||||||
"books/Computational Intelligence.txt",
|
|
||||||
"books/Concurrency in Go_ Tools and Techniques fo - Katherine Cox-Buday.txt",
|
|
||||||
"books/Concurrency in Go - Tools and Techniques for Developers.txt",
|
|
||||||
"books/ConsumersurplusatUber_PR.txt",
|
|
||||||
"books/Contagious - Why Things Catch On.txt",
|
|
||||||
"books/Conversion_Optimization.txt",
|
|
||||||
"books/cover - Jolcia.txt",
|
|
||||||
"books/Cplusplus_Today.txt",
|
|
||||||
"books/cprogramming_aselfteachingintroduction.txt",
|
|
||||||
"books/C Programming - PhD Rajiv Chopra.txt",
|
|
||||||
"books/Create a Data Driven Organization.txt",
|
|
||||||
"books/Creating a Data-Driven Organization - Carl Anderson.txt",
|
|
||||||
"books/Crossing the Chasm - Marketing and Selling Disruptive Products to Mainstream Customers.txt",
|
|
||||||
"books/Crypto101.txt",
|
|
||||||
"books/CSharpProfesional.txt",
|
|
||||||
"books/C_Sharp_Succinctly.txt",
|
|
||||||
"books/CSS3_Succinctly.txt",
|
|
||||||
"books/CSS3.txt",
|
|
||||||
"books/CSS FlexBox.txt",
|
|
||||||
"books/CssGridLayout.txt",
|
|
||||||
"books/CSS in Depth.txt",
|
|
||||||
"books/CSSMaster2ndEdition.txt",
|
|
||||||
"books/csspocketreference.txt",
|
|
||||||
"books/CSS-Programming-Cookbook.txt",
|
|
||||||
"books/CSS_Secrets.txt",
|
|
||||||
"books/css_thedefinitiveguide.txt",
|
|
||||||
"books/CursoHTML5.txt",
|
|
||||||
"books/D3-Tips-and-Tricks.txt",
|
|
||||||
"books/Daily Rituals - How Great Minds Make Time, Find Inspiration, and Get to Work.txt",
|
|
||||||
"books/Dark Web Exploring and Data Mining the Dark Side of the Web.txt",
|
|
||||||
"books/Dart-A-Modern-Web-Language.txt",
|
|
||||||
"books/dart_in_action.txt",
|
|
||||||
"books/dart_programming_tutorial.txt",
|
|
||||||
"books/Data_Algorithms (1).txt",
|
|
||||||
"books/Data_Algorithms.txt",
|
|
||||||
"books/Data Analysis with Open Source Tools.txt",
|
|
||||||
"books/Data_Analytics_in_Sports.txt",
|
|
||||||
"books/Data_Analytics_with_Hadoop (1).txt",
|
|
||||||
"books/datacleaning_pocketprimer.txt",
|
|
||||||
"books/data-driven.txt",
|
|
||||||
"books/datalog2011-dedalus.txt",
|
|
||||||
"books/Data Mashups in R.txt",
|
|
||||||
"books/Data Mining and Statistics for Decision Making.txt",
|
|
||||||
"books/DataMining-ch1.txt",
|
|
||||||
"books/DataMining-ch2.txt",
|
|
||||||
"books/DataMining-ch3.txt",
|
|
||||||
"books/DataMining-ch4.txt",
|
|
||||||
"books/DataMining-ch4 - Unknown.txt",
|
|
||||||
"books/DataMining-ch5.txt",
|
|
||||||
"books/DataMining-ch6.txt",
|
|
||||||
"books/DataMining-ch7.txt",
|
|
||||||
"books/Datamining-ch8.txt",
|
|
||||||
"books/Data Mining Concepts and Techniques.txt",
|
|
||||||
"books/Data Mining - Concepts, Models, Methods, and Algorithms.txt",
|
|
||||||
"books/Data Mining Methods for Recommender Systems.txt",
|
|
||||||
"books/Data Mining - Practical Machine Learning Tools and Techniques.txt",
|
|
||||||
"books/datamining.txt",
|
|
||||||
"books/Data_Science_from_Scratch (1).txt",
|
|
||||||
"books/Data Science from Scratch - Joel Grus.txt",
|
|
||||||
"books/Data Science from Scratch.txt",
|
|
||||||
"books/Data_Science_from_Scratch.txt",
|
|
||||||
"books/Data Source Handbook.txt",
|
|
||||||
"books/Data Stream Mining - A Practical Approach.txt",
|
|
||||||
"books/Data Structures and Algorithms.txt",
|
|
||||||
"books/Data Structures and Algorithms with JavaScript.txt",
|
|
||||||
"books/datastyle.txt",
|
|
||||||
"books/Data Visualization with D3.js Cookbook.txt",
|
|
||||||
"books/datavisualizationwithpythonandjavascript.txt",
|
|
||||||
"books/Data_Visualization_with_Python_and_JavaScript.txt",
|
|
||||||
"books/data-wrangling-cheatsheet.txt",
|
|
||||||
"books/Data Wrangling with JavaScript.txt",
|
|
||||||
"books/Data_Wrangling_with_Python (1).txt",
|
|
||||||
"books/dbSecurityBook.txt",
|
|
||||||
"books/DE_0_PHYTON -.txt",
|
|
||||||
"books/Dealing with China - An Insider Unmasks the New Economic Superpower.txt",
|
|
||||||
"books/DebianHandBookSpanish.txt",
|
|
||||||
"books/Debugging Teams - Better Productivity through Collaboration.txt",
|
|
||||||
"books/Decision Support Systems For Business Intelligence.txt",
|
|
||||||
"books/Deep Work - Cal Newport.txt",
|
|
||||||
"books/Deep Work - Rules for Focused Success in a Distracted World.txt",
|
|
||||||
"books/Dependency Injection Principles, Practices, and Patterns.txt",
|
|
||||||
"books/Design Driven Testing.txt",
|
|
||||||
"books/Design for How People Think - Using Brain Science to Build Better Products.txt",
|
|
||||||
"books/Designing Data-Intensive Applications - The Big Ideas Behind Reliable, Scalable and Maintainable Systems.txt",
|
|
||||||
"books/Designing_Data_Intensive_Applications.txt",
|
|
||||||
"books/Designing Data-Intensive Web Applications.txt",
|
|
||||||
"books/Designing Data Visualizations.txt",
|
|
||||||
"books/Designing Interfaces - Patterns for Effective Interaction Design.txt",
|
|
||||||
"books/DesigningUXForms.txt",
|
|
||||||
"books/designingwebapis.txt",
|
|
||||||
"books/Designing with the Mind in Mind Simple Guide to Understanding User Interface Design Rules.txt",
|
|
||||||
"books/Designing with the Mind in Mind Simple Gui - Unknown.txt",
|
|
||||||
"books/DesignPatterns.txt",
|
|
||||||
"books/developer-testing.txt",
|
|
||||||
"books/Developing Large Web Applications.txt",
|
|
||||||
"books/developing-microservices-node-js.txt",
|
|
||||||
"books/Developing Microservices with Node.js.txt",
|
|
||||||
"books/devops-2-0-toolkit.txt",
|
|
||||||
"books/devops-2-1-toolkit-deploying-monitoring.txt",
|
|
||||||
"books/DevOps Automation Cookbook.txt",
|
|
||||||
"books/devops-web-development.txt",
|
|
||||||
"books/DiveIntoPython.txt",
|
|
||||||
"books/django2webdevelopmentcookbook.txt",
|
|
||||||
"books/docker-bootcamp.txt",
|
|
||||||
"books/DockerContainerizationCookbook.txt",
|
|
||||||
"books/docker-cookbook-solutions-examples.txt",
|
|
||||||
"books/docker-in-action.txt",
|
|
||||||
"books/Docker in Action.txt",
|
|
||||||
"books/docker-in-practice.txt",
|
|
||||||
"books/Docker in Practice.txt",
|
|
||||||
"books/docker-orchestration.txt",
|
|
||||||
"books/Docker_ Up and Running - Matthias, Karl.txt",
|
|
||||||
"books/Docker Up and Running.txt",
|
|
||||||
"books/Docker_Up_and_Running.txt",
|
|
||||||
"books/domain-driven-design-distilled.txt",
|
|
||||||
"books/DotNETCore.txt",
|
|
||||||
"books/ECMAScript_6.txt",
|
|
||||||
"books/effectiveawk.txt",
|
|
||||||
"books/Effective_DevOps.txt",
|
|
||||||
"books/EF JS sonsivri.txt",
|
|
||||||
"books/Elasticsearch Blueprints - A practical project-based guide to generating compelling search solutions using the dynamic and powerful features of Elasticsearch.txt",
|
|
||||||
"books/Elasticsearch Cookbook.txt",
|
|
||||||
"books/Elasticsearch in Action.txt",
|
|
||||||
"books/Elasticsearch Indexing - Improve search experiences with Elasticsearch's powerful indexing functionality.txt",
|
|
||||||
"books/Elasticsearch Server.txt",
|
|
||||||
"books/Elasticsearch - The Definitive Guide.txt",
|
|
||||||
"books/ElasticSearchTutorial.txt",
|
|
||||||
"books/elasticsearch.txt",
|
|
||||||
"books/ElectronGettingStarted.txt",
|
|
||||||
"books/Electron in Action.txt",
|
|
||||||
"books/ElectronQuickIntro.txt",
|
|
||||||
"books/Elegant_SciPy.txt",
|
|
||||||
"books/Elixir in Action.txt",
|
|
||||||
"books/Elm Accelerated - James Porter.txt",
|
|
||||||
"books/Eloquent_JavaScript.txt",
|
|
||||||
"books/ELS2015.txt",
|
|
||||||
"books/embeddedvision.txt",
|
|
||||||
"books/Emergent Web Intelligence Advanced Information Retrieval.txt",
|
|
||||||
"books/Emergent Web Intelligence Advanced Semantic Technologies.txt",
|
|
||||||
"books/Enterprise_Big_Data_Lake (1).txt",
|
|
||||||
"books/EntityFrameworkCodeFirst.txt",
|
|
||||||
"books/EntityFrameworkCore.txt",
|
|
||||||
"books/entrepreneur revolution.txt",
|
|
||||||
"books/Eric Ries - The Lean Startup.txt",
|
|
||||||
"books/ESLII_print10.txt",
|
|
||||||
"books/Essential JavaScript Design Patterns.txt",
|
|
||||||
"books/eurosys10-boom 2.txt",
|
|
||||||
"books/eurosys10-boom.txt",
|
|
||||||
"books/expert-javascript.txt",
|
|
||||||
"books/expertpythonprogramming - Unknown.txt",
|
|
||||||
"books/Exploring Design Pattern For Dummies.txt",
|
|
||||||
"books/express-in-action.txt",
|
|
||||||
"books/Facebook - A Focus on Efficieny.txt",
|
|
||||||
"books/fashioning-data.txt",
|
|
||||||
"books/fcdae.txt",
|
|
||||||
"books/Feature Engineering for Machine Learning - Principles and Techniques for Data Scientists.txt",
|
|
||||||
"books/FlaskReleaseMarch03-2017.txt",
|
|
||||||
"books/flaskwebdevelopment.txt",
|
|
||||||
"books/Fluent_Python.txt",
|
|
||||||
"books/Foundations_for_Analytics_with_Python.txt",
|
|
||||||
"books/Foundations for Architecting Data Solutions - Managing Successful Data Projects.txt",
|
|
||||||
"books/Framing-Analytics-Requirements-v5.13.txt",
|
|
||||||
"books/FRIED_Jason_-_Rework.txt",
|
|
||||||
"books/FullStackJsDevelopmentWithMEAN.txt",
|
|
||||||
"books/Fundamentals of Data Visualization - A Primer on Making Informative and Compelling Figures.txt",
|
|
||||||
"books/gamedevelopmentusingpython.txt",
|
|
||||||
"books/GameProgrammingForKids.txt",
|
|
||||||
"books/Gaussian Processes for Machine Learning - Carl Edward Rasmussen.txt",
|
|
||||||
"books/Getting_Data_Right_Ch04_PE_Tamr.txt",
|
|
||||||
"books/getting-started-kubernetes-2nd.txt",
|
|
||||||
"books/GettingStartedWithASP.Net4.5WebForms.txt",
|
|
||||||
"books/Getting Started with Kubernetes.txt",
|
|
||||||
"books/Getting Started with Kudu - Jean-Marc Spaggiari.txt",
|
|
||||||
"books/GettingStartedWithLINQPad.txt",
|
|
||||||
"books/GettingStartedWithReactJs.txt",
|
|
||||||
"books/Getting Started with RStudio.txt",
|
|
||||||
"books/Getting Started with Storm.txt",
|
|
||||||
"books/Getting Started with TensorFlow.txt",
|
|
||||||
"books/Git - Giant Undo Button.txt",
|
|
||||||
"books/Git Internals.txt",
|
|
||||||
"books/GitInternals.txt",
|
|
||||||
"books/Git Internals - Unknown.txt",
|
|
||||||
"books/Global UX Design and Research in a Connected World.txt",
|
|
||||||
"books/GNU_C_LibraryReferenceManual.txt",
|
|
||||||
"books/GNULinuxAdvancedAdminstration.txt",
|
|
||||||
"books/go-building-web-applications.txt",
|
|
||||||
"books/go-design-patterns.txt",
|
|
||||||
"books/go-in-action.txt",
|
|
||||||
"books/go-in-practice.txt",
|
|
||||||
"books/go-programming-blueprints-2nd.txt",
|
|
||||||
"books/Go Recipes.txt",
|
|
||||||
"books/go.txt",
|
|
||||||
"books/go-web-programming.txt",
|
|
||||||
"books/Graph Algorithms - Practical Examples in Apache Spark and Neo4j.txt",
|
|
||||||
"books/Graph Databases - Ian Robinson, Jim Webber.txt",
|
|
||||||
"books/Graph Databases.txt",
|
|
||||||
"books/GraphDatabases.txt",
|
|
||||||
"books/Graphics of Large Datasets.txt",
|
|
||||||
"books/greppocketref.txt",
|
|
||||||
"books/Grokking Deep Learning.txt",
|
|
||||||
"books/GrowthHacking.txt",
|
|
||||||
"books/gsl_stats.txt",
|
|
||||||
"books/Hackers and Painters.txt",
|
|
||||||
"books/Hadoop in the Enterprise - Architecture - A Guide to Successful Integration.txt",
|
|
||||||
"books/Hadoop_Security.txt",
|
|
||||||
"books/Hadoop_ The Definitive Guide - Tom White.txt",
|
|
||||||
"books/Hadoop - The Definitive Guide.txt",
|
|
||||||
"books/hadoop-what-you-need-to-know.txt",
|
|
||||||
"books/hadoop-with-python.txt",
|
|
||||||
"books/HAL.txt",
|
|
||||||
"books/Handbook_Pt1.txt",
|
|
||||||
"books/Handbook_Pt2.txt",
|
|
||||||
"books/Handbook_Pt3.txt",
|
|
||||||
"books/Handbook_Pt4.txt",
|
|
||||||
"books/Hands-On Design Patterns with React Native - Mateusz Grzesiukiewicz.txt",
|
|
||||||
"books/hands-onfullstackdevelopmentwithspringboot20andreact.txt",
|
|
||||||
"books/hands-onfullstackwebdevelopmentwithangular6andlaravel5.txt",
|
|
||||||
"books/Hands-on Machine Learning with Scikit-Lear - Aurelien Geron.txt",
|
|
||||||
"books/Hands-On Machine Learning with Scikit Learn and TensorFlow - Concepts, Tools, and Techniques to Build Intelligent Systems.txt",
|
|
||||||
"books/Hands_On_Machine_Learning_with_Scikit_Learn_and_TensorFlow.txt",
|
|
||||||
"books/Hands On Machine Learning with Scikit Learn, Keras, and Tensorflow - Concepts, Tools, and Techniques to Build Intelligent Systems (Updated Release).txt",
|
|
||||||
"books/hdlwithdigitaldesign.txt",
|
|
||||||
"books/HeadFirstC.txt",
|
|
||||||
"books/Healing With Herbs and Spices_ Heal Your B - Simone McGrath.txt",
|
|
||||||
"books/HelloAndroid.txt",
|
|
||||||
"books/highperformanceimages.txt",
|
|
||||||
"books/High Performance JavaScript.txt",
|
|
||||||
"books/High_Performance_Mobile_Web.txt",
|
|
||||||
"books/HowToBuildAndScaleWithMicroServices.txt",
|
|
||||||
"books/HowToBuildAndScaleWithMicroServices - Unknown.txt",
|
|
||||||
"books/How to Live Forever - Alok Jha.txt",
|
|
||||||
"books/How to Pass Exams - Dominic O'Brien.txt",
|
|
||||||
"books/HTML5 and JavaScript Web Apps.txt",
|
|
||||||
"books/HTML5 Architecture.txt",
|
|
||||||
"books/HTML5CanvasReference.txt",
|
|
||||||
"books/HTML5 Canvas.txt",
|
|
||||||
"books/HTML5 Cookbook.txt",
|
|
||||||
"books/HTML5 & CSS3 FOR THE REAL WORLD.txt",
|
|
||||||
"books/HTML5-Programming-Cookbook.txt",
|
|
||||||
"books/HTML5SecurityCheatSheet.txt",
|
|
||||||
"books/HTML5_Vulnerabilities.txt",
|
|
||||||
"books/htmlcss2sample.txt",
|
|
||||||
"books/HTTP - 2 in Action.txt",
|
|
||||||
"books/human javascript - Henrik Joreteg.txt",
|
|
||||||
"books/Human JavaScript.txt",
|
|
||||||
"books/Identity and Data Security for Web Development Best Practices.txt",
|
|
||||||
"books/I Heart Logs Event Data, Stream Processing, and Data Integration.txt",
|
|
||||||
"books/Information Architecture For the Web and Beyond.txt",
|
|
||||||
"books/Information_Architecture_Fourth_Edition.txt",
|
|
||||||
"books/Information Theory, Inference, and Learning Algorithms .txt",
|
|
||||||
"books/Innovations in Classification, Data Science, and Information Systems.txt",
|
|
||||||
"books/Interactive Data Visualization for the Web.txt",
|
|
||||||
"books/Interactive_Data_Visualization_for_the_Web.txt",
|
|
||||||
"books/Interactive Data Visualization for the Web - Unknown.txt",
|
|
||||||
"books/Interview Preparations Kit - Software Engineer.txt",
|
|
||||||
"books/IntouchScriptingAndLogicGuide.txt",
|
|
||||||
"books/Introducing-Go.txt",
|
|
||||||
"books/introducingregularexpressions.txt",
|
|
||||||
"books/introduction-machine-learning-python.txt",
|
|
||||||
"books/introductionto3dgameprogrammingwithdirectx12.txt",
|
|
||||||
"books/Introduction to Docker.txt",
|
|
||||||
"books/IntroductionToDocker.txt",
|
|
||||||
"books/IntroductionToLinux.txt",
|
|
||||||
"books/IntroductionToNginx.txt",
|
|
||||||
"books/IntroToCrypto.txt",
|
|
||||||
"books/InventYourOwnGamesWithPython.txt",
|
|
||||||
"books/IPSUR.txt",
|
|
||||||
"books/ISLR Fourth Printing.txt",
|
|
||||||
"books/Java2.txt",
|
|
||||||
"books/JavaDesignPatterns.txt",
|
|
||||||
"books/JavaDevelopmentOnLnx.txt",
|
|
||||||
"books/JavaFXCookBook.txt",
|
|
||||||
"books/Java-JDBC.txt",
|
|
||||||
"books/JavaMultithreadingAndConcurrency.txt",
|
|
||||||
"books/JavaNIOCookbook.txt",
|
|
||||||
"books/JavaPersistenceAPI.txt",
|
|
||||||
"books/JavaScript A Beginners Guide .txt",
|
|
||||||
"books/JavaScript Cookbook.txt",
|
|
||||||
"books/JavaScriptInterviewQuestions.txt",
|
|
||||||
"books/JavaScript Patterns.txt",
|
|
||||||
"books/JavaScript_Succinctly.txt",
|
|
||||||
"books/JavaScript The Definitive Guide.txt",
|
|
||||||
"books/javascript_the_good_parts.txt",
|
|
||||||
"books/JavaScript The Good Parts.txt",
|
|
||||||
"books/JavaScript Web Applications.txt",
|
|
||||||
"books/JavaStartingIntoHibernate.txt",
|
|
||||||
"books/Java-ThinkJava.txt",
|
|
||||||
"books/Jenkins 2 - Up and Running - Evolve Your Deployment Pipeline for Next Generation Automation.txt",
|
|
||||||
"books/jenkins-the-definitive-guide.txt",
|
|
||||||
"books/JQueryHost.txt",
|
|
||||||
"books/JQueryNoviceToNinja.txt",
|
|
||||||
"books/jQuery_Succinctly.txt",
|
|
||||||
"books/JsNoviceToNinja2ndEdition.txt",
|
|
||||||
"books/JsNoviceToNinja.txt",
|
|
||||||
"books/Jurans Quality Handbook.txt",
|
|
||||||
"books/Kafka Streams in Action - Real time apps and microservices with the Kafka Streaming API.txt",
|
|
||||||
"books/Kafka - The Definitive Guide - Real Time Data and Stream Processing at Scale.txt",
|
|
||||||
"books/Kafka - The Definitive Guide.txt",
|
|
||||||
"books/Kubernetes Cookbook.txt",
|
|
||||||
"books/Kubernetes in Action.txt",
|
|
||||||
"books/Kubernetes Management Design Patterns With Docker, CoreOS Linux, and Other Platforms.txt",
|
|
||||||
"books/Kubernetes Microservices with Docker.txt",
|
|
||||||
"books/Kubernetes-Microservices with Docker.txt",
|
|
||||||
"books/Kuhn_Johnson_Applied_Predictive_Modeling.txt",
|
|
||||||
"books/LaBibliaDeMySQL.pdf.txt",
|
|
||||||
"books/lazy-analysts-guide-to-faster-sql.txt",
|
|
||||||
"books/Lean_Analytics.txt",
|
|
||||||
"books/Lean Customer Development.txt",
|
|
||||||
"books/Lean Enterprise.txt",
|
|
||||||
"books/Lean UX.txt",
|
|
||||||
"books/Learn Functional Programming by Implementing SQL with Underscore.js Presentation.txt",
|
|
||||||
"books/Learning_Agile.txt",
|
|
||||||
"books/Learning Apache Kafka.txt",
|
|
||||||
"books/Learning Apache Kafka - Unknown.txt",
|
|
||||||
"books/Learning Chaos Engineering - Russ Miles.txt",
|
|
||||||
"books/learningconcurrencyinpython - Unknown.txt",
|
|
||||||
"books/Learning Docker.txt",
|
|
||||||
"books/Learning ELK Stack - Build mesmerizing visualizations, analytics, and logs from your data using Elasticsearch, Logstash, and Kibana.txt",
|
|
||||||
"books/learninggnuemacs_3rdedition.txt",
|
|
||||||
"books/learninggraphql.txt",
|
|
||||||
"books/LearningJavaByBuildingAndroidGames.txt",
|
|
||||||
"books/Learning.Java_Oreilly_4th.Edition_Jun.2013.txt",
|
|
||||||
"books/Learning Java - Patrick Niemeyer.txt",
|
|
||||||
"books/Learning JavaScript Design Patterns.txt",
|
|
||||||
"books/learningjavascript.txt",
|
|
||||||
"books/Learning Java.txt",
|
|
||||||
"books/learningjquery3.txt",
|
|
||||||
"books/learningnodejsdevelopment.txt",
|
|
||||||
"books/learningphpmysqlandjavascript.txt",
|
|
||||||
"books/Learning Python, 5th Edition.txt",
|
|
||||||
"books/Learning Python - Mark Lutz.txt",
|
|
||||||
"books/Learning Python - Powerful Object-Oriented Programming.txt",
|
|
||||||
"books/LearningPython.txt",
|
|
||||||
"books/learningreact1.txt",
|
|
||||||
"books/learningroboticsusingpython - Unknown.txt",
|
|
||||||
"books/Learning Single-page Web Application Development.txt",
|
|
||||||
"books/Learning Spark.txt",
|
|
||||||
"books/Learning_Spark.txt",
|
|
||||||
"books/Learning_Swift.txt",
|
|
||||||
"books/learningthebashshell_3rdedition.txt",
|
|
||||||
"books/learningtheviandvimeditors_7thedition.txt",
|
|
||||||
"books/Learning Website Development with Django.txt",
|
|
||||||
"books/learnqt5.txt",
|
|
||||||
"books/lecture-22.txt",
|
|
||||||
"books/Linear Algebra Explained In Four Pages.txt",
|
|
||||||
"books/Linear Algebra.txt",
|
|
||||||
"books/Linked Data - Evolving The Web Into A Global Data Space.txt",
|
|
||||||
"books/Linked Open Data - The Essentials.txt",
|
|
||||||
"books/Linux Bible.txt",
|
|
||||||
"books/Linux Colección completa (2004).txt",
|
|
||||||
"books/LinuxCommandLineSheet.txt",
|
|
||||||
"books/LinuxCookBook.pdf - Pankaj Kumar.txt",
|
|
||||||
"books/linuxdevicedrivers.txt",
|
|
||||||
"books/LinuxEmbeddedDevelopment.txt",
|
|
||||||
"books/Linux From Scratch.txt",
|
|
||||||
"books/LinuxFromScratch.txt",
|
|
||||||
"books/linuxinanutshell.txt",
|
|
||||||
"books/Linux Internals_ Como funciona - Daniel Ezquerra.txt",
|
|
||||||
"books/LinuxKali.txt",
|
|
||||||
"books/LinuxNetworkingCookbook.txt",
|
|
||||||
"books/linuxpocketguide_3rdedition.txt",
|
|
||||||
"books/Linux Pocket.txt",
|
|
||||||
"books/LinuxPracticalSecurityCookBook.txt",
|
|
||||||
"books/LinuxShellScripting.txt",
|
|
||||||
"books/linuxsystemprogramming.txt",
|
|
||||||
"books/LittleInferenceBook.txt",
|
|
||||||
"books/Machine Learning Cheat Sheet.txt",
|
|
||||||
"books/Machine learning for hackers.txt",
|
|
||||||
"books/Machine Learning for Hackers.txt",
|
|
||||||
"books/Machine_Learning_with_R_Second_Edition.txt",
|
|
||||||
"books/Machine Learning with Spark.txt",
|
|
||||||
"books/Machine Learning with TensorFlow.txt",
|
|
||||||
"books/Maintainable JavaScript.txt",
|
|
||||||
"books/Making Isometric Social Real-Time Games with HTML5 CSS3 and JavaScript.txt",
|
|
||||||
"books/Management 3.0; Leading Agile Developers, - Jurgen Appelo.txt",
|
|
||||||
"books/ManualDePowerBuilder.txt",
|
|
||||||
"books/ManualDeSEO.txt",
|
|
||||||
"books/mapping-big-data.txt",
|
|
||||||
"books/MapReduce Design Patterns - Building Effective Algorithms and Analytics for Hadoop and Other Systems.txt",
|
|
||||||
"books/Mastering-Advanced-Analytics-With-Apache-Spark.txt",
|
|
||||||
"books/Mastering_Dart__Master_the_art_of.txt",
|
|
||||||
"books/Mastering ElasticSearch - Extend your knowledge on ElasticSearch, and querying and data handling, along with its internal workings.txt",
|
|
||||||
"books/masteringios12programming.txt",
|
|
||||||
"books/Mastering Kubernetes.txt",
|
|
||||||
"books/masteringmodularjavascript.txt",
|
|
||||||
"books/Mastering Modular JavaScript.txt",
|
|
||||||
"books/masteringpythonnetworking - Unknown.txt",
|
|
||||||
"books/masteringpython - Unknown.txt",
|
|
||||||
"books/masteringregularexpressions.txt",
|
|
||||||
"books/Mastering Regular Expressions.txt",
|
|
||||||
"books/Mastering Web Application Development with Express.txt",
|
|
||||||
"books/masteringxamarinuidevelopment.txt",
|
|
||||||
"books/mesos-in-action.txt",
|
|
||||||
"books/microservices-building-scalable-software.txt",
|
|
||||||
"books/microservices-deployment-cookbook.txt",
|
|
||||||
"books/Microservices Designing Deploying.txt",
|
|
||||||
"books/microservices-docker-microsoft-azure.txt",
|
|
||||||
"books/microservices-flexible-software-architecture.txt",
|
|
||||||
"books/microservices-from-day-one.txt",
|
|
||||||
"books/Microservices Patterns - With examples in Java.txt",
|
|
||||||
"books/microsoftaccess2019programmingwithvbaxmlandasp.txt",
|
|
||||||
"books/microsoftexcel2019programmingwithvbaxmlandasp.txt",
|
|
||||||
"books/microsoftexcelfunctionsandformulas_5e.txt",
|
|
||||||
"books/microsoftoffice2013_365andbeyond.txt",
|
|
||||||
"books/Mining Business Databases.txt",
|
|
||||||
"books/Mining of Data with Complex Structures.txt",
|
|
||||||
"books/Mining of Massive Datasets.txt",
|
|
||||||
"books/Mining Text Data.txt",
|
|
||||||
"books/Mining_the_Social_Web__Second_Edition (1).txt",
|
|
||||||
"books/Mining_the_Social_Web__Second_Edition.txt",
|
|
||||||
"books/Mining the Social Web.txt",
|
|
||||||
"books/Modeling With Data.txt",
|
|
||||||
"books/Modern Java in Action - Lambda, streams, functional and reactive programming.txt",
|
|
||||||
"books/ModernJs.txt",
|
|
||||||
"books/modernpythoncookbook - Unknown.txt",
|
|
||||||
"books/MongoDB3.txt",
|
|
||||||
"books/MongoDB - Applied Design Patterns, Practical Use Cases with the Leading NoSQL Database.txt",
|
|
||||||
"books/MongoDB Applied Design Patterns - Rick Copeland.txt",
|
|
||||||
"books/MongoDB Cookbook.txt",
|
|
||||||
"books/MongoDB - The Definitive Guide.txt",
|
|
||||||
"books/MongoDBTheDefinitiveGuide.txt",
|
|
||||||
"books/Monitoring with Graphite - Jason Dixon.txt",
|
|
||||||
"books/msexcel2016.txt",
|
|
||||||
"books/multimediawebdesignanddevelopment.txt",
|
|
||||||
"books/MySQLPluginDevelopmen.txt",
|
|
||||||
"books/native-docker-clustering-swarm.txt",
|
|
||||||
"books/Natural Language Annotation for Machine Learning.txt",
|
|
||||||
"books/Natural_Language_Annotation_for_Machine_Learning.txt",
|
|
||||||
"books/Natural Language Processing in Action - Understanding, analyzing, and generating text with Python.txt",
|
|
||||||
"books/Natural Language Processing with PyTorch - Build Intelligent Language Applications Using Deep Learning.txt",
|
|
||||||
"books/negron-muntaner-jennifers-butt.txt",
|
|
||||||
"books/NetworkProgrammingIndotNET.txt",
|
|
||||||
"books/Network_Security_Through_Data_Analysis.txt",
|
|
||||||
"books/New Trends in Computational Collective Intelligence.txt",
|
|
||||||
"books/Nodedotjs_Web_Development_Third_Edition.txt",
|
|
||||||
"books/Node for Front-End Developers.txt",
|
|
||||||
"books/NodeJsAdvancedGuide.txt",
|
|
||||||
"books/Node.js By Example.txt",
|
|
||||||
"books/Node.js Design Patterns.txt",
|
|
||||||
"books/Node.js in Action.txt",
|
|
||||||
"books/Node.js Recipes.txt",
|
|
||||||
"books/Node.js the Right Way.txt",
|
|
||||||
"books/NodeJs.txt",
|
|
||||||
"books/Node Up and Running.txt",
|
|
||||||
"books/Node- Up and Running.txt",
|
|
||||||
"books/NoSQLArchitectsGuide.txt",
|
|
||||||
"books/NoSQL Database Technology - A Survey and Comparison of Systems.txt",
|
|
||||||
"books/OraclePL-SQL3Edition.txt",
|
|
||||||
"books/OraclePL-SQL.txt",
|
|
||||||
"books/Oreilly.Beautiful.Data.Jul.2009.txt",
|
|
||||||
"books/O'Reilly Media -- Template for Microsoft W - na na.txt",
|
|
||||||
"books/OReilly.REST.API.Design.Rulebook.Oct.2011.ISBN.1449310508.txt",
|
|
||||||
"books/OReilly Twisted Network Programming Essentials 2nd Edition 2013.txt",
|
|
||||||
"books/Organizational_Profiles.txt",
|
|
||||||
"books/out-of-the-tar-pit.txt",
|
|
||||||
"books/PatternDesignInC++WithQt4.txt",
|
|
||||||
"books/PHP-And-MySql-NoviceToNinja.txt",
|
|
||||||
"books/Postgres.txt",
|
|
||||||
"books/Practical Cloud Security - A Guide for Secure Design and Deployment.txt",
|
|
||||||
"books/practicaldatacleaning.txt",
|
|
||||||
"books/Practical Machine Learning Tools and Techniques.txt",
|
|
||||||
"books/Practical Machine Learning.txt",
|
|
||||||
"books/Practical Node.js.txt",
|
|
||||||
"books/Practical Recommender Systems.txt",
|
|
||||||
"books/Practical Semantic Web and Linked Data Applications.txt",
|
|
||||||
"books/Practical_Statistics_for_Data_Scientists.txt",
|
|
||||||
"books/PrincipiosDeCompiladores1EraEdicion.txt",
|
|
||||||
"books/Principles of Data Quality.txt",
|
|
||||||
"books/Privacy and Big Data.txt",
|
|
||||||
"books/ProbStatBook.txt",
|
|
||||||
"books/pro-continuous-delivery-jenkins-2.txt",
|
|
||||||
"books/pro-docker.txt",
|
|
||||||
"books/Production Ready Microservices.txt",
|
|
||||||
"books/Pro Express.js.txt",
|
|
||||||
"books/Professional Node.js.txt",
|
|
||||||
"books/proGit.txt",
|
|
||||||
"books/Pro GIT.txt",
|
|
||||||
"books/ProgramacionEnC.txt",
|
|
||||||
"books/Programmable Logic Controller - Basic Prin - Lab-Volt.txt",
|
|
||||||
"books/Programming Hive - Edward Capriolo, Dean Wampler.txt",
|
|
||||||
"books/Programming HTML5 Applications.txt",
|
|
||||||
"books/Programming Kubernetes - michael Hausenblas.txt",
|
|
||||||
"books/Programming_Pig_Second_Edition.txt",
|
|
||||||
"books/Programming_Scala_Second_Edition.txt",
|
|
||||||
"books/Programming The Semantic Web.txt",
|
|
||||||
"books/Pro HTML5 Programming.txt",
|
|
||||||
"books/Pro JavaScript Design Patterns.txt",
|
|
||||||
"books/Pro .NET 2.0 Graphics Programming.txt",
|
|
||||||
"books/Pro Node.js for Developers.txt",
|
|
||||||
"books/Pro React.txt",
|
|
||||||
"books/Pro REST API Development with Node.js.txt",
|
|
||||||
"books/pro-vim-2014.txt",
|
|
||||||
"books/pynput.txt",
|
|
||||||
"books/py-quant-econ.txt",
|
|
||||||
"books/Python3CookBook.txt",
|
|
||||||
"books/python3_pocketprimer.txt",
|
|
||||||
"books/PythonBeginnerCheatSheet.txt",
|
|
||||||
"books/Python Cookbook, 2nd Edition.txt",
|
|
||||||
"books/Python Cookbook, 3rd Edition.txt",
|
|
||||||
"books/python-crash-course.txt",
|
|
||||||
"books/pythondataanalysiscookbook - Unknown.txt",
|
|
||||||
"books/Python Data Science Essentials.txt",
|
|
||||||
"books/pythondatascienceessentials - Unknown.txt",
|
|
||||||
"books/pythondatastructuresandalgorithms - Unknown.txt",
|
|
||||||
"books/Python Essential Reference.txt",
|
|
||||||
"books/PythonEssentialsCheatSheet.txt",
|
|
||||||
"books/Python for Data Analysis.txt",
|
|
||||||
"books/Python_for_Finance.txt",
|
|
||||||
"books/PythonGamesDevelopmentForBeginners.txt",
|
|
||||||
"books/Python GUI Programming Cookbook - Second Edition.txt",
|
|
||||||
"books/pythonguiprogrammingcookbook - Unknown.txt",
|
|
||||||
"books/Python GUI programming with Tkinter ( PDFDrive.com ) (2).txt",
|
|
||||||
"books/pythonhighperformance - Unknown.txt",
|
|
||||||
"books/Python_introduction.txt",
|
|
||||||
"books/Python Machine Learning Blueprints.txt",
|
|
||||||
"books/Python Machine Learning.txt",
|
|
||||||
"books/Python_Machine_Learning.txt",
|
|
||||||
"books/pythonmachinelearning - Unknown.txt",
|
|
||||||
"books/PythonMakingGamesWithPygame.txt",
|
|
||||||
"books/pythonmicroservicesdevelopment - Unknown.txt",
|
|
||||||
"books/Python-NetworkHacks.txt",
|
|
||||||
"books/python-pocket-reference-5th-edition.txt",
|
|
||||||
"books/pythonprogrammingwithraspberrypi - Unknown.txt",
|
|
||||||
"books/PythonTestingBeginnerGuide.txt",
|
|
||||||
"books/Python.Tkinter.Programming.txt",
|
|
||||||
"books/python-tricks.txt",
|
|
||||||
"books/PyWebScrapingBook.txt",
|
|
||||||
"books/Qt5 Python GUI Programming Cookbook_ Building responsive and powerful cross-platform applications with PyQt ( PDFDrive.com ).txt",
|
|
||||||
"books/quality-code-software-testing-principles-practices-and-patterns.txt",
|
|
||||||
"books/radziwill_statisticseasierwithr_preview.txt",
|
|
||||||
"books/randomforest2001.txt",
|
|
||||||
"books/R Cookbook - JD Long.txt",
|
|
||||||
"books/R_Cookbook.txt",
|
|
||||||
"books/R Data Structures and Algorithms.txt",
|
|
||||||
"books/R Deep Learning Cookbook.txt",
|
|
||||||
"books/reactandreactnative.txt",
|
|
||||||
"books/reactdesignpatternsandbestpractices.txt",
|
|
||||||
"books/Reactive Applications with Akka.Net.txt",
|
|
||||||
"books/Reactive Design Patterns.txt",
|
|
||||||
"books/ReactJs.txt",
|
|
||||||
"books/reactnativecookbook_ward.txt",
|
|
||||||
"books/React Native in Action.txt",
|
|
||||||
"books/REACT.txt",
|
|
||||||
"books/Real_Time_Big_Data_Analytics.txt",
|
|
||||||
"books/Real-World_Hadoop_MapR.txt",
|
|
||||||
"books/Redis Essentials.txt",
|
|
||||||
"books/Redis Essentials - Unknown.txt",
|
|
||||||
"books/Redis in Action.txt",
|
|
||||||
"books/Refactoring Improving the Design of Existing Code.txt",
|
|
||||||
"books/Regular Expression Pocket Reference.txt",
|
|
||||||
"books/Regular Expression Pocket Reference - Unknown.txt",
|
|
||||||
"books/Regular Expressions Cookbook.txt",
|
|
||||||
"books/RegularExpressions_Succinctly.txt",
|
|
||||||
"books/Relevant Search_ With applications for Sol - Doug Turnbull John Berryman.txt",
|
|
||||||
"books/Relevant Search - With applications for Solr and Elasticsearch.txt",
|
|
||||||
"books/ResponsiveDesign.txt",
|
|
||||||
"books/Responsive Web Design.txt",
|
|
||||||
"books/Responsive Web Design with HTML5 and CSS3.txt",
|
|
||||||
"books/rest-advanced-research-topics-and-practical-applications.txt",
|
|
||||||
"books/RESTful Java Patterns and Best Practices.txt",
|
|
||||||
"books/RESTful Java Web Services Security.txt",
|
|
||||||
"books/RESTful Java with JAX-RS 2.0, 2nd Edition.txt",
|
|
||||||
"books/RESTful Web API Design with Node.js.txt",
|
|
||||||
"books/RESTful Web APIs.txt",
|
|
||||||
"books/RESTful Web Clients - Enabling Reuse Through Hypermedia.txt",
|
|
||||||
"books/RESTful_Web_Services.txt",
|
|
||||||
"books/RESTful Web Services with Dropwizard.txt",
|
|
||||||
"books/Rexer_Analytics_2013_Data_Miner_Survey_Summary_Report.txt",
|
|
||||||
"books/RFP Proyecto CRM - Herve Cayard.txt",
|
|
||||||
"books/R_in_Action_Second__v15_MEAP.txt",
|
|
||||||
"books/R in a Nutshell, 2nd Edition.txt",
|
|
||||||
"books/Roy Cohn Part 01 of 01.txt",
|
|
||||||
"books/R_Packages.txt",
|
|
||||||
"books/R_ProgrammingSuccinctly.txt",
|
|
||||||
"books/rprogramming.txt",
|
|
||||||
"books/running-lean-iterate-from-plan-a-to-a-plan-that-works-lean-series.txt",
|
|
||||||
"books/RW.txt",
|
|
||||||
"books/Rxjs in Action.txt",
|
|
||||||
"books/Scala_Cookbook.txt",
|
|
||||||
"books/scala-test-driven-development.txt",
|
|
||||||
"books/Schema Matching and Mapping.txt",
|
|
||||||
"books/Secrets of the JavaScript Ninja.txt",
|
|
||||||
"books/Securing Devops - Safe Services in the Cloud.txt",
|
|
||||||
"books/sedandawk.txt",
|
|
||||||
"books/Semantic Web for the Working Ontologist.txt",
|
|
||||||
"books/Semantic Web for the Working Ontologist - Unknown.txt",
|
|
||||||
"books/Semantic Web Programming.txt",
|
|
||||||
"books/Semantic Web Services For Web Databases.txt",
|
|
||||||
"books/Semantic Web Services.txt",
|
|
||||||
"books/Semantic Web Technologies for Business Intelligence.txt",
|
|
||||||
"books/Site Reliability Engineering - How Google Runs Production Systems.txt",
|
|
||||||
"books/Slides - Communicating to Company.txt",
|
|
||||||
"books/Slides - How to Market.txt",
|
|
||||||
"books/Slides - How to Turn Feature Ideas Into User Stories.txt",
|
|
||||||
"books/Slides - Talking to Customers.txt",
|
|
||||||
"books/Slides - User Stories to Actual Features.txt",
|
|
||||||
"books/Slides - What do Product Managers Do.txt",
|
|
||||||
"books/Slides - What I Did As a Product Manager.txt",
|
|
||||||
"books/Slides - What Is Agile Development.txt",
|
|
||||||
"books/Slides - WhosOnTheTeam.txt",
|
|
||||||
"books/Slides - Working With Developers.txt",
|
|
||||||
"books/SLS_corrected_1.4.16.txt",
|
|
||||||
"books/SmashingNodeJs.txt",
|
|
||||||
"books/socc2012_bloom_lattices.txt",
|
|
||||||
"books/Social Data Mining.txt",
|
|
||||||
"books/softwarearchitecturewithpython - Unknown.txt",
|
|
||||||
"books/SoftwareDesignPatterns.txt",
|
|
||||||
"books/software-paradox.txt",
|
|
||||||
"books/software takes command.txt",
|
|
||||||
"books/softwaretestingprinciplesandpractices.txt",
|
|
||||||
"books/SoftwareTesting.txt",
|
|
||||||
"books/SolidPrinciples.txt",
|
|
||||||
"books/S.O.L.I.D_Principles.txt",
|
|
||||||
"books/Spark in Action.txt",
|
|
||||||
"books/Spark - The Definitive Guide - Big Data Processing Made Simple.txt",
|
|
||||||
"books/spatialEpiBook.txt",
|
|
||||||
"books/Speed Reading for Professionals - Mantesh.txt",
|
|
||||||
"books/spring5designpatterns.txt",
|
|
||||||
"books/SQL.Cookbook.2005.txt",
|
|
||||||
"books/StartingIntoAzure.txt",
|
|
||||||
"books/StartingIntoCouchDB.txt",
|
|
||||||
"books/StartingIntoGIT.txt",
|
|
||||||
"books/StartingIntoHTML5.txt",
|
|
||||||
"books/StartingIntoIonic.txt",
|
|
||||||
"books/StartingIntoMySQL.txt",
|
|
||||||
"books/StartingIntoNodeJs.txt",
|
|
||||||
"books/StartingIntoPHPEnvironment.txt",
|
|
||||||
"books/StartingIntoPLC_Programming.txt",
|
|
||||||
"books/StartingIntoXamarinForms.txt",
|
|
||||||
"books/StartUpBestPractices.txt",
|
|
||||||
"books/steve_jobs_walter_isaacson.txt",
|
|
||||||
"books/Streaming Data - Understanding the Real Time Pipeline.txt",
|
|
||||||
"books/Stunning CSS3 A project-based guide to the latest in CSS.txt",
|
|
||||||
"books/Swift.txt",
|
|
||||||
"books/t._cormen_-_introduction_to_algorithms_3rd_edition.txt",
|
|
||||||
"books/tdd-ebook-sample.txt",
|
|
||||||
"books/tensorflow2.txt",
|
|
||||||
"books/TensorFlow for Machine Intelligence - A Hands-On Introduction to Learning Algorithms.txt",
|
|
||||||
"books/TensorFlow Machine Learning Cookbook.txt",
|
|
||||||
"books/Testable JavaScript.txt",
|
|
||||||
"books/Test-Driven JavaScript Development 2.txt",
|
|
||||||
"books/Test Driven.txt",
|
|
||||||
"books/Testing Angular Applications.txt",
|
|
||||||
"books/Text Mining Classification, Clustering, and Applications.txt",
|
|
||||||
"books/TextMiningO.txt",
|
|
||||||
"books/TheArt&ScienceOfJS.txt",
|
|
||||||
"books/The Bastard Operator From Hell.txt",
|
|
||||||
"books/TheBeginnersGuideToNoSQL.txt",
|
|
||||||
"books/The Clean Coder - A Code of Conduct for Professional Programmers.txt",
|
|
||||||
"books/The CSS3 Anthology.txt",
|
|
||||||
"books/The Dart Programming Language.txt",
|
|
||||||
"books/The Data Analytics Handbook.txt",
|
|
||||||
"books/The Data Science Book.txt",
|
|
||||||
"books/The Design of Everyday Things.txt",
|
|
||||||
"books/The DevOps 2.0 Toolkit - Automating the Continuous Deployment Pipeline with Containerized Microservices.txt",
|
|
||||||
"books/The DevOps Adoption Playbook - A Guide to Adopting DevOps in a Multi-Speed IT Enterprise.txt",
|
|
||||||
"books/the-docker-book.txt",
|
|
||||||
"books/The Elements of Statistical Learning - Data Mining, Inference, and Prediction.txt",
|
|
||||||
"books/The Elements of Statistical Learning.txt",
|
|
||||||
"books/The Enterprise Big Data Lake - Delivering the Promise of Big Data and Data Science.txt",
|
|
||||||
"books/The Essential Guide to User Interface Design.txt",
|
|
||||||
"books/The_GNU_Debbuger.txt",
|
|
||||||
"books/The.Go.Programming.Language.txt",
|
|
||||||
"books/The Grammar of Graphics.txt",
|
|
||||||
"books/TheGuideToWireFraming.txt",
|
|
||||||
"books/The Laws of Simplicity.txt",
|
|
||||||
"books/the-lean-mindset-ask-the-right-questions.txt",
|
|
||||||
"books/The Lean Startup - How Today's Entrepreneurs Use Continuous Innovation to Create Radically Successful Businesses.txt",
|
|
||||||
"books/TheLinuxDevelopmentPlatform.txt",
|
|
||||||
"books/TheLinuxKernelModuleProgrammingGuid.txt",
|
|
||||||
"books/TheLinuxProgrammingInterface.txt",
|
|
||||||
"books/The Lion Way - Machine Learning plus Intelligent Optimization.txt",
|
|
||||||
"books/The Meaning of Tingo_ And Other Extraordin - Adam Jacot De Boinod.txt",
|
|
||||||
"books/The Minto Pyramid Principle - Logic in Writing, Thinking, & Problem Solving.txt",
|
|
||||||
"books/The Pragmatic Programmer From Journeyman to Master.txt",
|
|
||||||
"books/The Pragmatic Programmer.txt",
|
|
||||||
"books/The Principles of Beautiful Web Design.txt",
|
|
||||||
"books/The Site Reliability Workbook - Practical Ways to Implement SRE.txt",
|
|
||||||
"books/The Startup Owner s Manual_ The Step-by-Step Guide for Building a Great Company - Blank, Steve.txt",
|
|
||||||
"books/TheUltimateGuideToPrototyping.txt",
|
|
||||||
"books/Think Bayes - Bayesian Statistics Made Simple.txt",
|
|
||||||
"books/thinkbayes.txt",
|
|
||||||
"books/thinkcomplexity.txt",
|
|
||||||
"books/Think Like a Data Scientist. Tackle the data science process step by step.txt",
|
|
||||||
"books/Think Like a Programmer - An Intro. to Creative Problem Solving - V. Spraul (No Starch, 2012) BBS.txt",
|
|
||||||
"books/thinkpython.txt",
|
|
||||||
"books/Think Python.txt",
|
|
||||||
"books/thinkstats2.txt",
|
|
||||||
"books/Think Stats - Allen B. Downey.txt",
|
|
||||||
"books/Think Stats - Exploratory Data Analysis in Python.txt",
|
|
||||||
"books/thinkstats.txt",
|
|
||||||
"books/Think Stats.txt",
|
|
||||||
"books/Third-Party JavaScript.txt",
|
|
||||||
"books/tmux-taster-2014.txt",
|
|
||||||
"books/Transactions on Computational Collective I - Ngoc Thanh Nguyen (Editor).txt",
|
|
||||||
"books/Transactions on Computational Collective Intelligence III.txt",
|
|
||||||
"books/Transactions on Computational Collective Intelligence II.txt",
|
|
||||||
"books/Transactions on Computational Collective Intelligence I.txt",
|
|
||||||
"books/Transactions on Computational Collective Intelligence V.txt",
|
|
||||||
"books/Twitter_Bootstrap3_Succinctly.txt",
|
|
||||||
"books/TypeScript Design Patterns.txt",
|
|
||||||
"books/TypeScript.txt",
|
|
||||||
"books/UbuntuServerGuide.txt",
|
|
||||||
"books/understanding-chief-data-officer.txt",
|
|
||||||
"books/Understanding Computation - From Simple Machines to Impossible Programs.txt",
|
|
||||||
"books/UnderstandingDocker.txt",
|
|
||||||
"books/UnderstandingLinuxKernel3erEdition.txt",
|
|
||||||
"books/Understanding the Chief Data Officer - Unknown.txt",
|
|
||||||
"books/UnityGameDevelopment.txt",
|
|
||||||
"books/university-startups-and-spin-offs-guide-for-entrepreneurs-in-academia.txt",
|
|
||||||
"books/unixpowertools.txt",
|
|
||||||
"books/Unknown - Unknown.txt",
|
|
||||||
"books/User Interface Design for Programmers.txt",
|
|
||||||
"books/User Story Mapping - Discover the Whole Story, Build the Right Product.txt",
|
|
||||||
"books/using-asyncio-python-understanding-asynchronous.txt",
|
|
||||||
"books/Using AWS Lambda and Claudia.js.txt",
|
|
||||||
"books/using-docker.txt",
|
|
||||||
"books/Using Node.js for UI Testing.txt",
|
|
||||||
"books/usingsvgwithcss3andhtml5.txt",
|
|
||||||
"books/UX for Leaan Startups.txt",
|
|
||||||
"books/UX_Strategy.txt",
|
|
||||||
"books/VBAProfessionalTipsSecrets.txt",
|
|
||||||
"books/Version Control by Example.txt",
|
|
||||||
"books/Visualizing Data.txt",
|
|
||||||
"books/vuejs2designpatternsandbestpractices.txt",
|
|
||||||
"books/VueJs2.txt",
|
|
||||||
"books/vuejs_upandrunning.txt",
|
|
||||||
"books/Web Crawling and Data Mining with Apache Nutch.txt",
|
|
||||||
"books/Web Data Mining.txt",
|
|
||||||
"books/Web Development Recipes.txt",
|
|
||||||
"books/webdevelopmentwithdjangocookbook - Unknown.txt",
|
|
||||||
"books/Web Development with Node and Express.txt",
|
|
||||||
"books/Web Information Retrieval.txt",
|
|
||||||
"books/Web Mining and Social Networking Techniques and Applications.txt",
|
|
||||||
"books/Web Scraping with Python - Collecting More Data from the Modern Web.txt",
|
|
||||||
"books/why-startups-fail-and-how-yours-can-succeed.txt",
|
|
||||||
"books/Wiley - Pairs Trading - Quantitative Methods and Analysis.txt",
|
|
||||||
"books/wordpress5complete.txt",
|
|
||||||
"books/WPF.txt",
|
|
||||||
"books/youdontknowjs_es6andbeyond.txt",
|
|
||||||
"books/youdontknowjs_scopeandclosures.txt",
|
|
||||||
"books/youdontknowjs_upandgoing.txt",
|
|
||||||
"books/zero-one.txt",
|
|
||||||
"books/ZooKeeper - Distributed process coordination.txt",
|
|
||||||
];
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
|||||||
#![feature(let_chains)]
|
|
||||||
|
|
||||||
use rayon::prelude::*;
|
|
||||||
//use rayon::prelude::*;
|
|
||||||
use std::{env, fs};
|
|
||||||
|
|
||||||
fn clean_content(content: &str) -> String {
|
|
||||||
let alloed_ichars = "01234567891abcdefghijklmnopqrstuvwxyz \n.,!?";
|
|
||||||
|
|
||||||
let clean_content = content.chars()
|
|
||||||
.filter(|&c| alloed_ichars.contains(c))
|
|
||||||
.collect::<String>();
|
|
||||||
|
|
||||||
clean_content
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sentences(content: &str) -> usize {
|
|
||||||
let sentences = content.split('.')
|
|
||||||
.map(|s| s.trim_start()) // Remove leading whitespace
|
|
||||||
.count();
|
|
||||||
|
|
||||||
// // Remove last "sentence" if didn't end with a dot
|
|
||||||
// if let Some(last) = sentences.last() && !last.ends_with('.') {
|
|
||||||
// sentences.pop();
|
|
||||||
// }
|
|
||||||
|
|
||||||
sentences
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_words(content: &str, words: &mut usize, caps: &mut usize, fw: &mut usize) {
|
|
||||||
fn check_forbidden(w: &str) -> bool {
|
|
||||||
FORBIDDEN_WORDS.iter()
|
|
||||||
.find(|fw| str::eq_ignore_ascii_case(w, fw))
|
|
||||||
.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
for word in content.split_whitespace() {
|
|
||||||
*words += 1;
|
|
||||||
|
|
||||||
if is_fully_capitalized_word(word) {
|
|
||||||
*caps += 1;
|
|
||||||
}
|
|
||||||
if check_forbidden(word) {
|
|
||||||
*fw += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_fully_capitalized_word(word: &str) -> bool {
|
|
||||||
word.chars()
|
|
||||||
.all(|c| !c.is_ascii_alphanumeric() || c.is_ascii_uppercase())
|
|
||||||
}
|
|
||||||
|
|
||||||
//fn get_capitalized_words(content: &str) -> usize {
|
|
||||||
// let sentences = get_sentences(content);
|
|
||||||
//// let mut cap_words = vec![];
|
|
||||||
// let mut count = 0;
|
|
||||||
//
|
|
||||||
// for sentence in sentences {
|
|
||||||
// // Always skip the first word since sentences start with
|
|
||||||
// for word in get_words(sentence).skip(1) {
|
|
||||||
// if is_fully_capitalized_word(word) {
|
|
||||||
// count += 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// count
|
|
||||||
//}
|
|
||||||
|
|
||||||
fn get_numbers(clean_content: &str) -> usize {
|
|
||||||
clean_content.split(|c: char| !c.is_ascii_digit())
|
|
||||||
.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
//fn get_forbidden_words(content: &str) -> usize {
|
|
||||||
// fn check_forbidden(w: &str) -> bool {
|
|
||||||
// FORBIDDEN_WORDS.iter()
|
|
||||||
// .find(|fw| str::eq_ignore_ascii_case(w, fw))
|
|
||||||
// .is_some()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// get_words(content)
|
|
||||||
// .filter(|w| check_forbidden(w))
|
|
||||||
// .collect()
|
|
||||||
//}
|
|
||||||
|
|
||||||
fn analyze(data: &str) {
|
|
||||||
let clean_data = clean_content(data);
|
|
||||||
// drop(clean_data); // You aren't actually using clean_data :O
|
|
||||||
|
|
||||||
// All capitalized words
|
|
||||||
let mut words = 0;
|
|
||||||
let mut fw = 0;
|
|
||||||
let mut cap_words = 0;
|
|
||||||
get_words(&clean_data, &mut words, &mut fw, &mut cap_words);
|
|
||||||
|
|
||||||
println!("All capitalized words: {}", cap_words);
|
|
||||||
|
|
||||||
// All sentences
|
|
||||||
let sentences = get_sentences(data);
|
|
||||||
println!("Sentences: {}", sentences);
|
|
||||||
|
|
||||||
// All words
|
|
||||||
println!("Words: {}", words);
|
|
||||||
|
|
||||||
// Numbers
|
|
||||||
let numbers = get_numbers(&clean_data);
|
|
||||||
println!("Numbers: {}", numbers);
|
|
||||||
|
|
||||||
// Forbidden words
|
|
||||||
println!("Forbidden words: {}", fw);
|
|
||||||
|
|
||||||
if sentences > 0 {
|
|
||||||
let word_count_per_sentence = words / sentences;
|
|
||||||
println!("Word count per sentence: {}", word_count_per_sentence);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Read in files from args
|
|
||||||
let mut files = Vec::with_capacity(env::args().len());
|
|
||||||
let mut do_parallel = false;
|
|
||||||
|
|
||||||
for arg in env::args().skip(1) { // skip program arg
|
|
||||||
if arg == "-p" {
|
|
||||||
do_parallel = true;
|
|
||||||
} else {
|
|
||||||
files.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the work
|
|
||||||
let work = |file| {
|
|
||||||
let Ok(text) = fs::read_to_string(&file) else {
|
|
||||||
eprintln!("{file} isn't a valid file or couldn't be read");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
analyze(&text);
|
|
||||||
};
|
|
||||||
|
|
||||||
if !do_parallel {
|
|
||||||
files.iter().for_each(work);
|
|
||||||
} else {
|
|
||||||
files.par_iter().for_each(work)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FORBIDDEN_WORDS: &'static [&'static str] = &[
|
|
||||||
"recovery", "techie", "http", "https", "digital", "hack", "::", "//", "com",
|
|
||||||
"@", "crypto", "bitcoin", "wallet", "hacker", "welcome", "whatsapp", "email", "cryptocurrency",
|
|
||||||
"stolen", "freeze", "quick", "crucial", "tracing", "scammers", "expers", "hire", "century",
|
|
||||||
"transaction", "essential", "managing", "contact", "contacting", "understanding", "assets", "funds"
|
|
||||||
];
|
|
||||||
@@ -1,828 +0,0 @@
|
|||||||
#![feature(likely_unlikely)]
|
|
||||||
|
|
||||||
mod books;
|
|
||||||
|
|
||||||
use crate::books::FULL_BOOK_PATHS;
|
|
||||||
use core_affinity::CoreId;
|
|
||||||
use memmap2::Mmap;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::cell::OnceCell;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ffi::{OsStr, OsString};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::os::linux::raw::stat;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::thread::available_parallelism;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use std::{array, env, fs, hint, mem, process, thread};
|
|
||||||
use std::io::Read;
|
|
||||||
use libc::{aio_read, aiocb};
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_whitespace(b: u8) -> bool {
|
|
||||||
matches!(b, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_upper(b: u8) -> bool {
|
|
||||||
matches!(b, b'A'..=b'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_digit(b: u8) -> bool {
|
|
||||||
matches!(b, b'0'..=b'9')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(align(128))]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Stats {
|
|
||||||
pub sentences: u32,
|
|
||||||
pub words: u32,
|
|
||||||
pub capitalizeds: u32,
|
|
||||||
pub numbers: u32,
|
|
||||||
pub forbiddens: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
static TIME_SPENT_READING_FILES: Mutex<Duration> = Mutex::new(Duration::from_secs(0));
|
|
||||||
|
|
||||||
const TEMP_MEM_SIZE: usize = 6 * 1024 * 1024;
|
|
||||||
thread_local! {
|
|
||||||
static WORK_STATE: RefCell<WorkState> = RefCell::new(WorkState::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WorkState {
|
|
||||||
pub work_mem: Box<[u8]>,
|
|
||||||
// pub io_mem: Box<[u8]>,
|
|
||||||
// pub curr_read: Option<aiocb>,
|
|
||||||
// pub had_first_load: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkState {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
work_mem: vec![0; TEMP_MEM_SIZE].into_boxed_slice(),
|
|
||||||
// io_mem: vec![0; TEMP_MEM_SIZE].into_boxed_slice(),
|
|
||||||
// curr_read: None,
|
|
||||||
// had_first_load: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn work(file_path: &OsStr, stats: &mut Stats) {
|
|
||||||
WORK_STATE.with_borrow_mut(|state: &mut WorkState| {
|
|
||||||
// // Load file
|
|
||||||
// let start_time = Instant::now();
|
|
||||||
|
|
||||||
// let Ok(text) = fs::read(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// process::abort();
|
|
||||||
// };
|
|
||||||
|
|
||||||
let mut file = File::open(file_path).unwrap();
|
|
||||||
let file_len = file.metadata().unwrap().len() as usize;
|
|
||||||
file.read_exact(&mut state.work_mem[..file_len]).unwrap();
|
|
||||||
let text = &state.work_mem[..file_len];
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut cb = mem::zeroed();
|
|
||||||
|
|
||||||
aio_read(&raw mut cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// let text = include_bytes!("../../../books/Advanced Techniques in Web Intelligence – Part II.txt").as_slice();
|
|
||||||
|
|
||||||
// let time_reading = start_time.elapsed();
|
|
||||||
// {
|
|
||||||
// let mut guard = TIME_SPENT_READING_FILES.lock().unwrap();
|
|
||||||
// *guard += time_reading;
|
|
||||||
// }
|
|
||||||
|
|
||||||
analyze(&text, stats);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn analyze(text: &[u8], stats: &mut Stats) {
|
|
||||||
// // NOTE: mmap is quite a bit slower
|
|
||||||
// // Load file
|
|
||||||
// let Ok(file) = File::open(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// std::process::abort();
|
|
||||||
// };
|
|
||||||
// let mmap = unsafe {
|
|
||||||
// Mmap::map(&file).unwrap()
|
|
||||||
// };
|
|
||||||
// mem::forget(file);
|
|
||||||
// let text = &*mmap;
|
|
||||||
|
|
||||||
// // Load file
|
|
||||||
// let start_time = Instant::now();
|
|
||||||
// let Ok(text) = fs::read(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// process::abort();
|
|
||||||
// };
|
|
||||||
// let time_reading = start_time.elapsed();
|
|
||||||
// {
|
|
||||||
// let mut guard = TIME_SPENT_READING_FILES.lock().unwrap();
|
|
||||||
// *guard += time_reading;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let mut sentences = 0;
|
|
||||||
let mut words = 0;
|
|
||||||
let mut capitalizeds = 0;
|
|
||||||
let mut numbers = 0;
|
|
||||||
let mut forbiddens = 0;
|
|
||||||
|
|
||||||
let mut idx = 0;
|
|
||||||
'full_loop: loop {
|
|
||||||
// Skip whitespace
|
|
||||||
while is_ascii_whitespace(text[idx]) {
|
|
||||||
idx += 1;
|
|
||||||
if idx >= text.len() {
|
|
||||||
break 'full_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find end of word
|
|
||||||
let word_start = idx;
|
|
||||||
let mut has_non_upper = false;
|
|
||||||
|
|
||||||
'find_word_end: while let b = text[idx] && !is_ascii_whitespace(b) {
|
|
||||||
idx += 1;
|
|
||||||
if idx >= text.len() {
|
|
||||||
break 'find_word_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-char logic
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
if !is_ascii_upper(b) {
|
|
||||||
has_non_upper = true;
|
|
||||||
}
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let word = &text[word_start..idx];
|
|
||||||
|
|
||||||
// dbg!(str::from_utf8(word).unwrap());
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
if !has_non_upper {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check forbidden
|
|
||||||
if unsafe { FW_TAB.lookup(word) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
for token in text.split(|&b| is_ascii_whitespace(b)) {
|
|
||||||
if token.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
// Sentence count, folded into this loop
|
|
||||||
// instead of another loop (better cache usage)
|
|
||||||
for &b in token {
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if upper
|
|
||||||
if token.iter().all(|&b| is_ascii_upper(b)) {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
for &b in token {
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if words
|
|
||||||
// if FORBIDDEN_WORDS.contains(&token) {
|
|
||||||
// if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, token) } {
|
|
||||||
if unsafe { FW_TAB.lookup(token) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// NOTE: This is pretty slow:
|
|
||||||
let mut idx = 0;
|
|
||||||
let mut word_start = 0;
|
|
||||||
let mut is_in_word = false;
|
|
||||||
let mut has_non_upper = false;
|
|
||||||
loop {
|
|
||||||
let b = unsafe { *text.get_unchecked(idx) };
|
|
||||||
|
|
||||||
let mut process_word = false;
|
|
||||||
if is_ascii_whitespace(b) {
|
|
||||||
if is_in_word {
|
|
||||||
process_word = true;
|
|
||||||
|
|
||||||
// Reset state for next word
|
|
||||||
is_in_word = false;
|
|
||||||
has_non_upper = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !is_in_word {
|
|
||||||
word_start = idx;
|
|
||||||
is_in_word = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
has_non_upper |= !is_ascii_upper(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
// Check sentences
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let word = &text[word_start..idx];
|
|
||||||
|
|
||||||
idx += 1;
|
|
||||||
|
|
||||||
if process_word || idx >= text.len() {
|
|
||||||
words += 1;
|
|
||||||
if !has_non_upper {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// // DEBUG:
|
|
||||||
// println!("'{}'", str::from_utf8(word).unwrap());
|
|
||||||
|
|
||||||
if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, word) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if idx >= text.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
stats.sentences = sentences;
|
|
||||||
stats.words = words;
|
|
||||||
stats.capitalizeds = capitalizeds;
|
|
||||||
stats.numbers = numbers;
|
|
||||||
stats.forbiddens = forbiddens;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn analyze_old(file_path: &OsStr, stats: &mut Stats) {
|
|
||||||
// Load file
|
|
||||||
let Ok(text) = fs::read(file_path) else {
|
|
||||||
eprintln!("invalid file!");
|
|
||||||
std::process::abort();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut sentences = 0;
|
|
||||||
let mut words = 0;
|
|
||||||
let mut capitalizeds = 0;
|
|
||||||
let mut numbers = 0;
|
|
||||||
let mut forbiddens = 0;
|
|
||||||
|
|
||||||
for token in text.split(|&b| is_ascii_whitespace(b)) {
|
|
||||||
if token.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
// Sentence count, folded into this loop
|
|
||||||
// instead of another loop (better cache usage)
|
|
||||||
for &b in token {
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if upper
|
|
||||||
if token.iter().all(|&b| is_ascii_upper(b)) {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
for &b in token {
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if words
|
|
||||||
// if FORBIDDEN_WORDS.contains(&token) {
|
|
||||||
if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, token) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.sentences = sentences;
|
|
||||||
stats.words = words;
|
|
||||||
stats.capitalizeds = capitalizeds;
|
|
||||||
stats.numbers = numbers;
|
|
||||||
stats.forbiddens = forbiddens;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Read in files from args
|
|
||||||
let mut files = Vec::with_capacity(env::args().len());
|
|
||||||
let mut do_parallel = false;
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
for arg in env::args_os().skip(1) {
|
|
||||||
// skip program arg
|
|
||||||
if arg == "-p" {
|
|
||||||
do_parallel = true;
|
|
||||||
} else {
|
|
||||||
files.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("[PROFILE] taking args took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
// env::args_os().
|
|
||||||
|
|
||||||
// let files = FULL_BOOK_PATHS;
|
|
||||||
|
|
||||||
// // Build table
|
|
||||||
// let tab = FwTab::build();
|
|
||||||
// tab.compile();
|
|
||||||
|
|
||||||
// Do the work
|
|
||||||
let mut stats = vec![Stats {
|
|
||||||
sentences: 0,
|
|
||||||
words: 0,
|
|
||||||
capitalizeds: 0,
|
|
||||||
numbers: 0,
|
|
||||||
forbiddens: 0,
|
|
||||||
}; files.len()];
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let num_cores = available_parallelism().unwrap().get();
|
|
||||||
let num_threads = num_cores * 1;
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
dbg!(num_threads);
|
|
||||||
dbg!(num_cores);
|
|
||||||
|
|
||||||
rayon::ThreadPoolBuilder::new()
|
|
||||||
.num_threads(num_threads)
|
|
||||||
.build_global()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
files.par_iter()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(idx, p)| {
|
|
||||||
let s = unsafe {
|
|
||||||
&mut *stats.as_ptr()
|
|
||||||
.offset(idx as isize)
|
|
||||||
.cast_mut()
|
|
||||||
};
|
|
||||||
|
|
||||||
// let mut path = OsString::from("../../");
|
|
||||||
// path.push(p);
|
|
||||||
let path = p;
|
|
||||||
work(path, s);
|
|
||||||
});
|
|
||||||
|
|
||||||
// thread::scope(|scope| {
|
|
||||||
// let files_per_thread = files.len() / num_threads;
|
|
||||||
//
|
|
||||||
// for thread_idx in 0..num_threads {
|
|
||||||
// let capture_files = &files;
|
|
||||||
// let capture_stats = &stats;
|
|
||||||
// thread::Builder::new().spawn_scoped(scope, move || {
|
|
||||||
// let files = capture_files;
|
|
||||||
// let stats = capture_stats;
|
|
||||||
//
|
|
||||||
// // Set thread affinity
|
|
||||||
// assert!(core_affinity::set_for_current(CoreId { id: thread_idx % num_cores }));
|
|
||||||
//
|
|
||||||
// // Do work
|
|
||||||
// let thread_start = thread_idx * files_per_thread;
|
|
||||||
// for i in 0..files_per_thread {
|
|
||||||
// let real_idx = thread_start + i;
|
|
||||||
// let file_path = &files[real_idx];
|
|
||||||
// let st = unsafe {
|
|
||||||
// &mut *stats.as_ptr()
|
|
||||||
// .offset(real_idx as isize)
|
|
||||||
// .cast_mut()
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// work(&file_path, st);
|
|
||||||
// }
|
|
||||||
// }).unwrap();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
println!("[PROFILE] processing text took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
// Accumulate stats
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let mut total_words = 0;
|
|
||||||
let mut total_capitalizeds = 0;
|
|
||||||
let mut total_sentences = 0;
|
|
||||||
let mut total_numbers = 0;
|
|
||||||
let mut total_forbiddens = 0;
|
|
||||||
|
|
||||||
for stat in &stats {
|
|
||||||
total_words += stat.words;
|
|
||||||
total_capitalizeds += stat.capitalizeds;
|
|
||||||
total_sentences += stat.sentences;
|
|
||||||
total_numbers += stat.numbers;
|
|
||||||
total_forbiddens += stat.forbiddens;
|
|
||||||
}
|
|
||||||
|
|
||||||
let capitalized_percentage = (total_capitalizeds as f32 / total_words as f32) * 100.0;
|
|
||||||
let forbidden_percentage = (total_forbiddens as f32 / total_words as f32) * 100.0;
|
|
||||||
let word_count_per_sentence = total_words as f32 / total_sentences as f32;
|
|
||||||
|
|
||||||
println!();
|
|
||||||
println!("Total Words: {total_words}");
|
|
||||||
println!("Total Capitalized words: {total_capitalizeds}");
|
|
||||||
println!("Total Sentences: {total_sentences}");
|
|
||||||
println!("Total Numbers: {total_numbers}");
|
|
||||||
println!("Total Forbidden words: {total_forbiddens}");
|
|
||||||
println!("Capitalized percentage: {capitalized_percentage:.6}");
|
|
||||||
println!("Forbidden percentage: {forbidden_percentage:.6}");
|
|
||||||
println!("Word count per sentence: {word_count_per_sentence:.6}");
|
|
||||||
println!("Total files read: {}", files.len());
|
|
||||||
|
|
||||||
println!("[PROFILE] accumulating stats took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
println!("[PROFILE] total file reading took {:?}", &*TIME_SPENT_READING_FILES.lock().unwrap());
|
|
||||||
|
|
||||||
// Exit process to avoid running drops
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct FwTab {
|
|
||||||
// pub dir_and_len_bits: [u32; 256],
|
|
||||||
pub dir_len_bits: [u16; 256],
|
|
||||||
pub dir: [u8; 256],
|
|
||||||
pub strs: [u8; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FwTab {
|
|
||||||
pub fn build() -> Self {
|
|
||||||
// Sort fws by first char
|
|
||||||
let mut sorted_fws: Vec<Vec<&'static [u8]>> = vec![vec![]; 256];
|
|
||||||
|
|
||||||
for word in FORBIDDEN_WORDS {
|
|
||||||
sorted_fws[word[0] as usize].push(&word);
|
|
||||||
}
|
|
||||||
for i in 0..256 {
|
|
||||||
sorted_fws[i].sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// // DEBUG:
|
|
||||||
// println!("{:#?}", sorted_fws[b'@' as usize].iter().map(|s| str::from_utf8(s).unwrap()).collect::<Vec<_>>());
|
|
||||||
|
|
||||||
// Build str tab
|
|
||||||
let mut fw_dir = [0u8; 256];
|
|
||||||
// let mut fw_dir_len_bits = [0u32; 256];
|
|
||||||
let mut fw_dir_len_bits = [0u16; 256];
|
|
||||||
let mut fw_strs: Vec<u8> = vec![];
|
|
||||||
|
|
||||||
fw_strs.push(b'\0'); // push dummy value so that 0 in the dir means no-entries
|
|
||||||
|
|
||||||
for c in 0..256 {
|
|
||||||
for fw in FORBIDDEN_WORDS {
|
|
||||||
if c == fw[0] as usize {
|
|
||||||
fw_dir_len_bits[c] |= 0x1 << fw.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sorted_fws[c].is_empty() {
|
|
||||||
let sublist_start_offset = fw_strs.len().try_into().unwrap();
|
|
||||||
fw_dir[c] = sublist_start_offset;
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
println!("{c} start offset: {}", sublist_start_offset);
|
|
||||||
println!("{:#?}", sorted_fws[c].iter().map(|s| str::from_utf8(s).unwrap()).collect::<Vec<_>>());
|
|
||||||
|
|
||||||
// Push strings
|
|
||||||
for fw in &sorted_fws[c] {
|
|
||||||
fw_strs.push(fw.len().try_into().unwrap());
|
|
||||||
for &c in &fw[1..] {
|
|
||||||
fw_strs.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark end of per-char word sublist
|
|
||||||
fw_strs.push(b'\0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
println!("strs len: {}", fw_strs.len());
|
|
||||||
|
|
||||||
assert_eq!(fw_dir.len(), 256);
|
|
||||||
assert!(fw_strs.len() <= 256);
|
|
||||||
|
|
||||||
fw_strs.resize(256, 0);
|
|
||||||
|
|
||||||
let tab = FwTab {
|
|
||||||
dir: fw_dir,
|
|
||||||
dir_len_bits: fw_dir_len_bits,
|
|
||||||
// dir_and_len_bits: array::from_fn(|idx| {
|
|
||||||
// (fw_dir_len_bits[idx] & 0xff_ff_ff) | ((fw_dir[idx] as u32) << 24)
|
|
||||||
// }),
|
|
||||||
strs: fw_strs.try_into().unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// DEBUG: Test some strings
|
|
||||||
unsafe {
|
|
||||||
dbg!(tab.lookup(b"cpm"));
|
|
||||||
dbg!(tab.lookup(b"com"));
|
|
||||||
dbg!(tab.lookup(b"coma"));
|
|
||||||
dbg!(tab.lookup(b"co"));
|
|
||||||
dbg!(tab.lookup(b"cam"));
|
|
||||||
dbg!(tab.lookup(b"crypto"));
|
|
||||||
dbg!(tab.lookup(b"@"));
|
|
||||||
dbg!(tab.lookup(b""));
|
|
||||||
dbg!(tab.lookup(b" "));
|
|
||||||
dbg!(tab.lookup(b"test"));
|
|
||||||
dbg!(tab.lookup(b"expers"));
|
|
||||||
}
|
|
||||||
|
|
||||||
tab
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile(&self) {
|
|
||||||
println!("static FW_TAB: FwTab = FwTab {{");
|
|
||||||
|
|
||||||
// println!("\tdir_and_len_bits: [");
|
|
||||||
// for chunk in self.dir_and_len_bits.chunks(16) {
|
|
||||||
// print!("\t\t");
|
|
||||||
// for &b in chunk {
|
|
||||||
// print!("0x{b:08x}, ");
|
|
||||||
// }
|
|
||||||
// println!();
|
|
||||||
// }
|
|
||||||
// println!("\t],");
|
|
||||||
|
|
||||||
println!("\tdir: [");
|
|
||||||
for chunk in self.dir.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:02x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("\tdir_len_bits: [");
|
|
||||||
for chunk in self.dir_len_bits.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:04x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("\tstrs: [");
|
|
||||||
for chunk in self.strs.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:02x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("}};");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn lookup(&self, word: &[u8]) -> bool {
|
|
||||||
let &[first_char, ..] = word else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// let dir_and_len_bits = unsafe {
|
|
||||||
// *self.dir_and_len_bits.get_unchecked(first_char as usize)
|
|
||||||
// };
|
|
||||||
// if word.len() < 23 && ((dir_and_len_bits >> word.len()) & 0x1) == 0 {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let len_bits = unsafe {
|
|
||||||
*self.dir_len_bits.get_unchecked(first_char as usize)
|
|
||||||
};
|
|
||||||
if word.len() < 16 && ((len_bits >> word.len()) & 0x1) == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let mut str_offset = (dir_and_len_bits >> 24) as usize;
|
|
||||||
let mut str_offset = unsafe {
|
|
||||||
*self.dir.get_unchecked(first_char as usize) as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
// Char doesn't have any strings in the table
|
|
||||||
if str_offset == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over strs
|
|
||||||
loop {
|
|
||||||
// let fw_len = u16::from_le_bytes([
|
|
||||||
// self.strs[str_offset],
|
|
||||||
// self.strs[str_offset+1]
|
|
||||||
// ]);
|
|
||||||
let fw_len: u8 = unsafe {
|
|
||||||
*self.strs.get_unchecked(str_offset)
|
|
||||||
};
|
|
||||||
|
|
||||||
if fw_len == 0 {
|
|
||||||
// We've reached the end of the word sublist
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only compare words if they are the same length
|
|
||||||
if word.len() == fw_len as usize {
|
|
||||||
// Compare strs
|
|
||||||
let mut char_offset = 1usize;
|
|
||||||
loop {
|
|
||||||
// Found the word!
|
|
||||||
if char_offset == word.len() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fw_char = unsafe { *self.strs.get_unchecked(str_offset + char_offset) };
|
|
||||||
let word_char = unsafe { *word.get_unchecked(char_offset) };
|
|
||||||
|
|
||||||
if fw_char > word_char {
|
|
||||||
// Word can't possible be in the sorted list, return
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if fw_char < word_char {
|
|
||||||
// Try next word
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
char_offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance to next word
|
|
||||||
// let str_len_bytes = 2;
|
|
||||||
let str_len_bytes = 1;
|
|
||||||
str_offset += (fw_len as usize - 1) + str_len_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const FORBIDDEN_WORDS: [&'static [u8]; 35] = [
|
|
||||||
b"recovery",
|
|
||||||
b"techie",
|
|
||||||
b"http",
|
|
||||||
b"https",
|
|
||||||
b"digital",
|
|
||||||
b"hack",
|
|
||||||
b"::",
|
|
||||||
b"//",
|
|
||||||
b"com",
|
|
||||||
b"@",
|
|
||||||
b"crypto",
|
|
||||||
b"bitcoin",
|
|
||||||
b"wallet",
|
|
||||||
b"hacker",
|
|
||||||
b"welcome",
|
|
||||||
b"whatsapp",
|
|
||||||
b"email",
|
|
||||||
b"cryptocurrency",
|
|
||||||
b"stolen",
|
|
||||||
b"freeze",
|
|
||||||
b"quick",
|
|
||||||
b"crucial",
|
|
||||||
b"tracing",
|
|
||||||
b"scammers",
|
|
||||||
b"expers",
|
|
||||||
b"hire",
|
|
||||||
b"century",
|
|
||||||
b"transaction",
|
|
||||||
b"essential",
|
|
||||||
b"managing",
|
|
||||||
b"contact",
|
|
||||||
b"contacting",
|
|
||||||
b"understanding",
|
|
||||||
b"assets",
|
|
||||||
b"funds",
|
|
||||||
];
|
|
||||||
|
|
||||||
static FW_TAB: FwTab = FwTab {
|
|
||||||
dir: [
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x09, 0x10, 0x18, 0x4f, 0x57, 0x6c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00,
|
|
||||||
0x00, 0x99, 0x9f, 0xa8, 0xb7, 0xd0, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
],
|
|
||||||
dir_len_bits: [
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0040, 0x0080, 0x44c8, 0x0080, 0x0260, 0x0060, 0x0000, 0x0070, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0020, 0x0100, 0x0140, 0x08c0, 0x2000, 0x0000, 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
],
|
|
||||||
strs: [
|
|
||||||
0x00, 0x02, 0x2f, 0x00, 0x02, 0x3a, 0x00, 0x01, 0x00, 0x06, 0x73, 0x73, 0x65, 0x74, 0x73, 0x00,
|
|
||||||
0x07, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x00, 0x07, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x79, 0x03,
|
|
||||||
0x6f, 0x6d, 0x07, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
|
|
||||||
0x69, 0x6e, 0x67, 0x07, 0x72, 0x75, 0x63, 0x69, 0x61, 0x6c, 0x06, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
|
||||||
0x0e, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x07,
|
|
||||||
0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x00, 0x05, 0x6d, 0x61, 0x69, 0x6c, 0x09, 0x73, 0x73, 0x65,
|
|
||||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x06, 0x78, 0x70, 0x65, 0x72, 0x73, 0x00, 0x06, 0x72, 0x65, 0x65,
|
|
||||||
0x7a, 0x65, 0x05, 0x75, 0x6e, 0x64, 0x73, 0x00, 0x04, 0x61, 0x63, 0x6b, 0x06, 0x61, 0x63, 0x6b,
|
|
||||||
0x65, 0x72, 0x04, 0x69, 0x72, 0x65, 0x04, 0x74, 0x74, 0x70, 0x05, 0x74, 0x74, 0x70, 0x73, 0x00,
|
|
||||||
0x08, 0x61, 0x6e, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x00, 0x05, 0x75, 0x69, 0x63, 0x6b, 0x00, 0x08,
|
|
||||||
0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x00, 0x08, 0x63, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73,
|
|
||||||
0x06, 0x74, 0x6f, 0x6c, 0x65, 0x6e, 0x00, 0x06, 0x65, 0x63, 0x68, 0x69, 0x65, 0x07, 0x72, 0x61,
|
|
||||||
0x63, 0x69, 0x6e, 0x67, 0x0b, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
|
||||||
0x0d, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x06, 0x61,
|
|
||||||
0x6c, 0x6c, 0x65, 0x74, 0x07, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x08, 0x68, 0x61, 0x74, 0x73,
|
|
||||||
0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//static FW_TAB_DIR: [u8; 256] = [
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x09, 0x10, 0x18, 0x4f, 0x57, 0x6c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00,
|
|
||||||
// 0x00, 0x99, 0x9f, 0xa8, 0xb7, 0xd0, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
//];
|
|
||||||
//static FW_TAB_STRS: [u8; 244] = [
|
|
||||||
// 0x00, 0x02, 0x2f, 0x00, 0x02, 0x3a, 0x00, 0x01, 0x00, 0x06, 0x73, 0x73, 0x65, 0x74, 0x73, 0x00,
|
|
||||||
// 0x07, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x00, 0x07, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x79, 0x03,
|
|
||||||
// 0x6f, 0x6d, 0x07, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
|
|
||||||
// 0x69, 0x6e, 0x67, 0x07, 0x72, 0x75, 0x63, 0x69, 0x61, 0x6c, 0x06, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
|
||||||
// 0x0e, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x07,
|
|
||||||
// 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x00, 0x05, 0x6d, 0x61, 0x69, 0x6c, 0x09, 0x73, 0x73, 0x65,
|
|
||||||
// 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x06, 0x78, 0x70, 0x65, 0x72, 0x73, 0x00, 0x06, 0x72, 0x65, 0x65,
|
|
||||||
// 0x7a, 0x65, 0x05, 0x75, 0x6e, 0x64, 0x73, 0x00, 0x04, 0x61, 0x63, 0x6b, 0x06, 0x61, 0x63, 0x6b,
|
|
||||||
// 0x65, 0x72, 0x04, 0x69, 0x72, 0x65, 0x04, 0x74, 0x74, 0x70, 0x05, 0x74, 0x74, 0x70, 0x73, 0x00,
|
|
||||||
// 0x08, 0x61, 0x6e, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x00, 0x05, 0x75, 0x69, 0x63, 0x6b, 0x00, 0x08,
|
|
||||||
// 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x00, 0x08, 0x63, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73,
|
|
||||||
// 0x06, 0x74, 0x6f, 0x6c, 0x65, 0x6e, 0x00, 0x06, 0x65, 0x63, 0x68, 0x69, 0x65, 0x07, 0x72, 0x61,
|
|
||||||
// 0x63, 0x69, 0x6e, 0x67, 0x0b, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
|
||||||
// 0x0d, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x06, 0x61,
|
|
||||||
// 0x6c, 0x6c, 0x65, 0x74, 0x07, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x08, 0x68, 0x61, 0x74, 0x73,
|
|
||||||
// 0x61, 0x70, 0x70, 0x00,
|
|
||||||
//];
|
|
||||||
@@ -1,891 +0,0 @@
|
|||||||
#![feature(likely_unlikely)]
|
|
||||||
#![feature(rust_cold_cc)]
|
|
||||||
|
|
||||||
mod books;
|
|
||||||
|
|
||||||
use crate::books::FULL_BOOK_PATHS;
|
|
||||||
use core_affinity::CoreId;
|
|
||||||
use libc::{aio_read, aiocb, read};
|
|
||||||
use memmap2::Mmap;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::cell::OnceCell;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ffi::{OsStr, OsString};
|
|
||||||
use std::fs::{File, OpenOptions};
|
|
||||||
use std::io::Read;
|
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::os::linux::raw::stat;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::thread::available_parallelism;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use std::{array, env, fs, hint, mem, process, thread};
|
|
||||||
use std::hint::assert_unchecked;
|
|
||||||
use std::os::unix::fs::{FileExt, OpenOptionsExt};
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_whitespace(b: u8) -> bool {
|
|
||||||
matches!(b, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_upper(b: u8) -> bool {
|
|
||||||
matches!(b, b'A'..=b'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_digit(b: u8) -> bool {
|
|
||||||
matches!(b, b'0'..=b'9')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(align(128))]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Stats {
|
|
||||||
pub sentences: u32,
|
|
||||||
pub words: u32,
|
|
||||||
pub capitalizeds: u32,
|
|
||||||
pub numbers: u32,
|
|
||||||
pub forbiddens: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
static TIME_SPENT_READING_FILES: Mutex<Duration> = Mutex::new(Duration::from_secs(0));
|
|
||||||
|
|
||||||
const TEMP_MEM_SIZE: usize = 6 * 1024 * 1024;
|
|
||||||
thread_local! {
|
|
||||||
static WORK_STATE: RefCell<WorkState> = RefCell::new(WorkState::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WorkState {
|
|
||||||
pub work_mem: Box<[u8]>,
|
|
||||||
// pub io_mem: Box<[u8]>,
|
|
||||||
// pub curr_read: Option<aiocb>,
|
|
||||||
// pub had_first_load: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkState {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
work_mem: vec![0; TEMP_MEM_SIZE].into_boxed_slice(),
|
|
||||||
// io_mem: vec![0; TEMP_MEM_SIZE].into_boxed_slice(),
|
|
||||||
// curr_read: None,
|
|
||||||
// had_first_load: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cold]
|
|
||||||
#[inline(never)]
|
|
||||||
extern "rust-cold" fn die() -> ! {
|
|
||||||
println!("Something went wrong! I'm going to die now");
|
|
||||||
process::abort()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn work(file_path: &OsStr, stats: &mut Stats) {
|
|
||||||
WORK_STATE.with_borrow_mut(|state: &mut WorkState| {
|
|
||||||
// // Load file
|
|
||||||
// let start_time = Instant::now();
|
|
||||||
|
|
||||||
// let Ok(text) = fs::read(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// process::abort();
|
|
||||||
// };
|
|
||||||
|
|
||||||
// NOTE: Reading the file like this is noticeably faster!
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
// .custom_flags(libc::O_DIRECT) // O_DIRECT is A LOT slower!!
|
|
||||||
.open(file_path)
|
|
||||||
.unwrap_or_else(|_| die());
|
|
||||||
|
|
||||||
let mut read_offset = 0;
|
|
||||||
loop {
|
|
||||||
// let rb = file.read_at(&mut state.work_mem[read_offset..], read_offset as u64)
|
|
||||||
let rb = file.read(&mut state.work_mem[read_offset..])
|
|
||||||
.unwrap_or_else(|_| die());
|
|
||||||
|
|
||||||
if hint::unlikely(rb == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_offset += rb;
|
|
||||||
}
|
|
||||||
let text = &state.work_mem[..read_offset];
|
|
||||||
|
|
||||||
// file.read_exact(&mut state.work_mem[..file_len]).unwrap();
|
|
||||||
|
|
||||||
// let text = include_bytes!("../../../books/Advanced Techniques in Web Intelligence – Part II.txt").as_slice();
|
|
||||||
|
|
||||||
// let time_reading = start_time.elapsed();
|
|
||||||
// {
|
|
||||||
// let mut guard = TIME_SPENT_READING_FILES.lock().unwrap();
|
|
||||||
// *guard += time_reading;
|
|
||||||
// }
|
|
||||||
|
|
||||||
analyze(&text, stats);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn analyze(text: &[u8], stats: &mut Stats) {
|
|
||||||
// // NOTE: mmap is quite a bit slower
|
|
||||||
// // Load file
|
|
||||||
// let Ok(file) = File::open(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// std::process::abort();
|
|
||||||
// };
|
|
||||||
// let mmap = unsafe {
|
|
||||||
// Mmap::map(&file).unwrap()
|
|
||||||
// };
|
|
||||||
// mem::forget(file);
|
|
||||||
// let text = &*mmap;
|
|
||||||
|
|
||||||
// // Load file
|
|
||||||
// let start_time = Instant::now();
|
|
||||||
// let Ok(text) = fs::read(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// process::abort();
|
|
||||||
// };
|
|
||||||
// let time_reading = start_time.elapsed();
|
|
||||||
// {
|
|
||||||
// let mut guard = TIME_SPENT_READING_FILES.lock().unwrap();
|
|
||||||
// *guard += time_reading;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let mut sentences = 0;
|
|
||||||
let mut words = 0;
|
|
||||||
let mut capitalizeds = 0;
|
|
||||||
let mut numbers = 0;
|
|
||||||
let mut forbiddens = 0;
|
|
||||||
|
|
||||||
let mut idx = 0;
|
|
||||||
'full_loop: loop {
|
|
||||||
// Skip whitespace
|
|
||||||
while is_ascii_whitespace(text[idx]) {
|
|
||||||
idx += 1;
|
|
||||||
if hint::unlikely(idx >= text.len()) {
|
|
||||||
break 'full_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find end of word
|
|
||||||
let word_start = idx;
|
|
||||||
let mut has_non_upper = false;
|
|
||||||
|
|
||||||
'find_word_end: while let b = text[idx] && !is_ascii_whitespace(b) {
|
|
||||||
idx += 1;
|
|
||||||
if hint::unlikely(idx >= text.len()) {
|
|
||||||
break 'find_word_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-char logic
|
|
||||||
if !is_ascii_upper(b) {
|
|
||||||
has_non_upper = true;
|
|
||||||
}
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
// sentences += (b == b'.') as u32;
|
|
||||||
// numbers += is_ascii_digit(b) as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
let word = &text[word_start..idx];
|
|
||||||
|
|
||||||
// dbg!(str::from_utf8(word).unwrap());
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
if !has_non_upper {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check forbidden
|
|
||||||
if unsafe { FW_TAB.lookup(word) } {
|
|
||||||
// if FW_PHF.contains(word) { // phf is a lot slower than my FwTab
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
for token in text.split(|&b| is_ascii_whitespace(b)) {
|
|
||||||
if token.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
// Sentence count, folded into this loop
|
|
||||||
// instead of another loop (better cache usage)
|
|
||||||
for &b in token {
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if upper
|
|
||||||
if token.iter().all(|&b| is_ascii_upper(b)) {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
for &b in token {
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if words
|
|
||||||
// if FORBIDDEN_WORDS.contains(&token) {
|
|
||||||
// if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, token) } {
|
|
||||||
if unsafe { FW_TAB.lookup(token) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// NOTE: This is pretty slow:
|
|
||||||
let mut idx = 0;
|
|
||||||
let mut word_start = 0;
|
|
||||||
let mut is_in_word = false;
|
|
||||||
let mut has_non_upper = false;
|
|
||||||
loop {
|
|
||||||
let b = unsafe { *text.get_unchecked(idx) };
|
|
||||||
|
|
||||||
let mut process_word = false;
|
|
||||||
if is_ascii_whitespace(b) {
|
|
||||||
if is_in_word {
|
|
||||||
process_word = true;
|
|
||||||
|
|
||||||
// Reset state for next word
|
|
||||||
is_in_word = false;
|
|
||||||
has_non_upper = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !is_in_word {
|
|
||||||
word_start = idx;
|
|
||||||
is_in_word = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
has_non_upper |= !is_ascii_upper(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
// Check sentences
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let word = &text[word_start..idx];
|
|
||||||
|
|
||||||
idx += 1;
|
|
||||||
|
|
||||||
if process_word || idx >= text.len() {
|
|
||||||
words += 1;
|
|
||||||
if !has_non_upper {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// // DEBUG:
|
|
||||||
// println!("'{}'", str::from_utf8(word).unwrap());
|
|
||||||
|
|
||||||
if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, word) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if idx >= text.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
stats.sentences = sentences;
|
|
||||||
stats.words = words;
|
|
||||||
stats.capitalizeds = capitalizeds;
|
|
||||||
stats.numbers = numbers;
|
|
||||||
stats.forbiddens = forbiddens;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn analyze_old(file_path: &OsStr, stats: &mut Stats) {
|
|
||||||
// Load file
|
|
||||||
let Ok(text) = fs::read(file_path) else {
|
|
||||||
eprintln!("invalid file!");
|
|
||||||
std::process::abort();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut sentences = 0;
|
|
||||||
let mut words = 0;
|
|
||||||
let mut capitalizeds = 0;
|
|
||||||
let mut numbers = 0;
|
|
||||||
let mut forbiddens = 0;
|
|
||||||
|
|
||||||
for token in text.split(|&b| is_ascii_whitespace(b)) {
|
|
||||||
if token.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
// Sentence count, folded into this loop
|
|
||||||
// instead of another loop (better cache usage)
|
|
||||||
for &b in token {
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if upper
|
|
||||||
if token.iter().all(|&b| is_ascii_upper(b)) {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
for &b in token {
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if words
|
|
||||||
// if FORBIDDEN_WORDS.contains(&token) {
|
|
||||||
if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, token) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.sentences = sentences;
|
|
||||||
stats.words = words;
|
|
||||||
stats.capitalizeds = capitalizeds;
|
|
||||||
stats.numbers = numbers;
|
|
||||||
stats.forbiddens = forbiddens;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Read in files from args
|
|
||||||
let mut files = Vec::with_capacity(env::args().len());
|
|
||||||
let mut do_parallel = false;
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
for arg in env::args_os().skip(1) {
|
|
||||||
// skip program arg
|
|
||||||
if arg == "-p" {
|
|
||||||
do_parallel = true;
|
|
||||||
} else {
|
|
||||||
files.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("[PROFILE] taking args took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
// env::args_os().
|
|
||||||
|
|
||||||
// let files = FULL_BOOK_PATHS;
|
|
||||||
|
|
||||||
// // Build table
|
|
||||||
// let tab = FwTab::build();
|
|
||||||
// tab.compile();
|
|
||||||
|
|
||||||
// Do the work
|
|
||||||
let mut stats = vec![Stats {
|
|
||||||
sentences: 0,
|
|
||||||
words: 0,
|
|
||||||
capitalizeds: 0,
|
|
||||||
numbers: 0,
|
|
||||||
forbiddens: 0,
|
|
||||||
}; files.len()];
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let num_cores = available_parallelism().unwrap().get();
|
|
||||||
let num_threads = num_cores * 1;
|
|
||||||
|
|
||||||
// // DEBUG:
|
|
||||||
// dbg!(num_threads);
|
|
||||||
// dbg!(num_cores);
|
|
||||||
|
|
||||||
rayon::ThreadPoolBuilder::new()
|
|
||||||
.num_threads(num_threads)
|
|
||||||
.build_global()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
files.par_iter()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(idx, p)| {
|
|
||||||
let s = unsafe {
|
|
||||||
&mut *stats.as_ptr()
|
|
||||||
.offset(idx as isize)
|
|
||||||
.cast_mut()
|
|
||||||
};
|
|
||||||
|
|
||||||
// let mut path = OsString::from("../../");
|
|
||||||
// path.push(p);
|
|
||||||
let path = p;
|
|
||||||
work(path, s);
|
|
||||||
});
|
|
||||||
|
|
||||||
// thread::scope(|scope| {
|
|
||||||
// let files_per_thread = files.len() / num_threads;
|
|
||||||
//
|
|
||||||
// for thread_idx in 0..num_threads {
|
|
||||||
// let capture_files = &files;
|
|
||||||
// let capture_stats = &stats;
|
|
||||||
// thread::Builder::new().spawn_scoped(scope, move || {
|
|
||||||
// let files = capture_files;
|
|
||||||
// let stats = capture_stats;
|
|
||||||
//
|
|
||||||
// // Set thread affinity
|
|
||||||
// assert!(core_affinity::set_for_current(CoreId { id: thread_idx % num_cores }));
|
|
||||||
//
|
|
||||||
// // Do work
|
|
||||||
// let thread_start = thread_idx * files_per_thread;
|
|
||||||
// for i in 0..files_per_thread {
|
|
||||||
// let real_idx = thread_start + i;
|
|
||||||
// let file_path = &files[real_idx];
|
|
||||||
// let st = unsafe {
|
|
||||||
// &mut *stats.as_ptr()
|
|
||||||
// .offset(real_idx as isize)
|
|
||||||
// .cast_mut()
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// work(&file_path, st);
|
|
||||||
// }
|
|
||||||
// }).unwrap();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
println!("[PROFILE] processing text took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
// Accumulate stats
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let mut total_words = 0;
|
|
||||||
let mut total_capitalizeds = 0;
|
|
||||||
let mut total_sentences = 0;
|
|
||||||
let mut total_numbers = 0;
|
|
||||||
let mut total_forbiddens = 0;
|
|
||||||
|
|
||||||
for stat in &stats {
|
|
||||||
total_words += stat.words;
|
|
||||||
total_capitalizeds += stat.capitalizeds;
|
|
||||||
total_sentences += stat.sentences;
|
|
||||||
total_numbers += stat.numbers;
|
|
||||||
total_forbiddens += stat.forbiddens;
|
|
||||||
}
|
|
||||||
|
|
||||||
let capitalized_percentage = (total_capitalizeds as f32 / total_words as f32) * 100.0;
|
|
||||||
let forbidden_percentage = (total_forbiddens as f32 / total_words as f32) * 100.0;
|
|
||||||
let word_count_per_sentence = total_words as f32 / total_sentences as f32;
|
|
||||||
|
|
||||||
println!();
|
|
||||||
println!("Total Words: {total_words}");
|
|
||||||
println!("Total Capitalized words: {total_capitalizeds}");
|
|
||||||
println!("Total Sentences: {total_sentences}");
|
|
||||||
println!("Total Numbers: {total_numbers}");
|
|
||||||
println!("Total Forbidden words: {total_forbiddens}");
|
|
||||||
println!("Capitalized percentage: {capitalized_percentage:.6}");
|
|
||||||
println!("Forbidden percentage: {forbidden_percentage:.6}");
|
|
||||||
println!("Word count per sentence: {word_count_per_sentence:.6}");
|
|
||||||
println!("Total files read: {}", files.len());
|
|
||||||
|
|
||||||
println!("[PROFILE] accumulating stats took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
println!("[PROFILE] total file reading took {:?}", &*TIME_SPENT_READING_FILES.lock().unwrap());
|
|
||||||
|
|
||||||
// Exit process to avoid running drops
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct FwTab {
|
|
||||||
// pub dir_and_len_bits: [u32; 256],
|
|
||||||
pub dir_len_bits: [u16; 256],
|
|
||||||
pub dir: [u8; 256],
|
|
||||||
pub strs: [u8; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FwTab {
|
|
||||||
pub fn build() -> Self {
|
|
||||||
// Sort fws by first char
|
|
||||||
let mut sorted_fws: Vec<Vec<&'static [u8]>> = vec![vec![]; 256];
|
|
||||||
|
|
||||||
for word in FORBIDDEN_WORDS {
|
|
||||||
sorted_fws[word[0] as usize].push(&word);
|
|
||||||
}
|
|
||||||
for i in 0..256 {
|
|
||||||
sorted_fws[i].sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// // DEBUG:
|
|
||||||
// println!("{:#?}", sorted_fws[b'@' as usize].iter().map(|s| str::from_utf8(s).unwrap()).collect::<Vec<_>>());
|
|
||||||
|
|
||||||
// Build str tab
|
|
||||||
let mut fw_dir = [0u8; 256];
|
|
||||||
// let mut fw_dir_len_bits = [0u32; 256];
|
|
||||||
let mut fw_dir_len_bits = [0u16; 256];
|
|
||||||
let mut fw_strs: Vec<u8> = vec![];
|
|
||||||
|
|
||||||
fw_strs.push(b'\0'); // push dummy value so that 0 in the dir means no-entries
|
|
||||||
|
|
||||||
for c in 0..256 {
|
|
||||||
for fw in FORBIDDEN_WORDS {
|
|
||||||
if c == fw[0] as usize {
|
|
||||||
fw_dir_len_bits[c] |= 0x1 << fw.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sorted_fws[c].is_empty() {
|
|
||||||
let sublist_start_offset = fw_strs.len().try_into().unwrap();
|
|
||||||
fw_dir[c] = sublist_start_offset;
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
println!("{c} start offset: {}", sublist_start_offset);
|
|
||||||
println!("{:#?}", sorted_fws[c].iter().map(|s| str::from_utf8(s).unwrap()).collect::<Vec<_>>());
|
|
||||||
|
|
||||||
// Push strings
|
|
||||||
for fw in &sorted_fws[c] {
|
|
||||||
fw_strs.push(fw.len().try_into().unwrap());
|
|
||||||
for &c in &fw[1..] {
|
|
||||||
fw_strs.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark end of per-char word sublist
|
|
||||||
fw_strs.push(b'\0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
println!("strs len: {}", fw_strs.len());
|
|
||||||
|
|
||||||
assert_eq!(fw_dir.len(), 256);
|
|
||||||
assert!(fw_strs.len() <= 256);
|
|
||||||
|
|
||||||
fw_strs.resize(256, 0);
|
|
||||||
|
|
||||||
let tab = FwTab {
|
|
||||||
dir: fw_dir,
|
|
||||||
dir_len_bits: fw_dir_len_bits,
|
|
||||||
// dir_and_len_bits: array::from_fn(|idx| {
|
|
||||||
// (fw_dir_len_bits[idx] & 0xff_ff_ff) | ((fw_dir[idx] as u32) << 24)
|
|
||||||
// }),
|
|
||||||
strs: fw_strs.try_into().unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// DEBUG: Test some strings
|
|
||||||
unsafe {
|
|
||||||
dbg!(tab.lookup(b"cpm"));
|
|
||||||
dbg!(tab.lookup(b"com"));
|
|
||||||
dbg!(tab.lookup(b"coma"));
|
|
||||||
dbg!(tab.lookup(b"co"));
|
|
||||||
dbg!(tab.lookup(b"cam"));
|
|
||||||
dbg!(tab.lookup(b"crypto"));
|
|
||||||
dbg!(tab.lookup(b"@"));
|
|
||||||
dbg!(tab.lookup(b""));
|
|
||||||
dbg!(tab.lookup(b" "));
|
|
||||||
dbg!(tab.lookup(b"test"));
|
|
||||||
dbg!(tab.lookup(b"expers"));
|
|
||||||
}
|
|
||||||
|
|
||||||
tab
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile(&self) {
|
|
||||||
println!("static FW_TAB: FwTab = FwTab {{");
|
|
||||||
|
|
||||||
// println!("\tdir_and_len_bits: [");
|
|
||||||
// for chunk in self.dir_and_len_bits.chunks(16) {
|
|
||||||
// print!("\t\t");
|
|
||||||
// for &b in chunk {
|
|
||||||
// print!("0x{b:08x}, ");
|
|
||||||
// }
|
|
||||||
// println!();
|
|
||||||
// }
|
|
||||||
// println!("\t],");
|
|
||||||
|
|
||||||
println!("\tdir: [");
|
|
||||||
for chunk in self.dir.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:02x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("\tdir_len_bits: [");
|
|
||||||
for chunk in self.dir_len_bits.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:04x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("\tstrs: [");
|
|
||||||
for chunk in self.strs.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:02x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("}};");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn lookup(&self, word: &[u8]) -> bool {
|
|
||||||
// let &[first_char, ..] = word else {
|
|
||||||
// return false;
|
|
||||||
// };
|
|
||||||
let first_char = unsafe { *word.get_unchecked(0) };
|
|
||||||
|
|
||||||
// let dir_and_len_bits = unsafe {
|
|
||||||
// *self.dir_and_len_bits.get_unchecked(first_char as usize)
|
|
||||||
// };
|
|
||||||
// if word.len() < 23 && ((dir_and_len_bits >> word.len()) & 0x1) == 0 {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let len_bits = unsafe {
|
|
||||||
*self.dir_len_bits.get_unchecked(first_char as usize)
|
|
||||||
};
|
|
||||||
if hint::likely(word.len() < 16 && ((len_bits >> word.len()) & 0x1) == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let mut str_offset = (dir_and_len_bits >> 24) as usize;
|
|
||||||
let mut str_offset = unsafe {
|
|
||||||
*self.dir.get_unchecked(first_char as usize) as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
// Char doesn't have any strings in the table
|
|
||||||
if str_offset == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over strs
|
|
||||||
loop {
|
|
||||||
// let fw_len = u16::from_le_bytes([
|
|
||||||
// self.strs[str_offset],
|
|
||||||
// self.strs[str_offset+1]
|
|
||||||
// ]);
|
|
||||||
let fw_len: u8 = unsafe {
|
|
||||||
*self.strs.get_unchecked(str_offset)
|
|
||||||
};
|
|
||||||
|
|
||||||
if fw_len == 0 {
|
|
||||||
// We've reached the end of the word sublist
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only compare words if they are the same length
|
|
||||||
if hint::unlikely(word.len() == fw_len as usize) {
|
|
||||||
// Compare strs
|
|
||||||
let mut char_offset = 1usize;
|
|
||||||
loop {
|
|
||||||
// Found the word!
|
|
||||||
if char_offset == word.len() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fw_char = unsafe { *self.strs.get_unchecked(str_offset + char_offset) };
|
|
||||||
let word_char = unsafe { *word.get_unchecked(char_offset) };
|
|
||||||
|
|
||||||
if fw_char > word_char {
|
|
||||||
// Word can't possible be in the sorted list, return
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if fw_char < word_char {
|
|
||||||
// Try next word
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
char_offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance to next word
|
|
||||||
// let str_len_bytes = 2;
|
|
||||||
let str_len_bytes = 1;
|
|
||||||
str_offset += (fw_len as usize - 1) + str_len_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const FORBIDDEN_WORDS: [&'static [u8]; 35] = [
|
|
||||||
b"recovery",
|
|
||||||
b"techie",
|
|
||||||
b"http",
|
|
||||||
b"https",
|
|
||||||
b"digital",
|
|
||||||
b"hack",
|
|
||||||
b"::",
|
|
||||||
b"//",
|
|
||||||
b"com",
|
|
||||||
b"@",
|
|
||||||
b"crypto",
|
|
||||||
b"bitcoin",
|
|
||||||
b"wallet",
|
|
||||||
b"hacker",
|
|
||||||
b"welcome",
|
|
||||||
b"whatsapp",
|
|
||||||
b"email",
|
|
||||||
b"cryptocurrency",
|
|
||||||
b"stolen",
|
|
||||||
b"freeze",
|
|
||||||
b"quick",
|
|
||||||
b"crucial",
|
|
||||||
b"tracing",
|
|
||||||
b"scammers",
|
|
||||||
b"expers",
|
|
||||||
b"hire",
|
|
||||||
b"century",
|
|
||||||
b"transaction",
|
|
||||||
b"essential",
|
|
||||||
b"managing",
|
|
||||||
b"contact",
|
|
||||||
b"contacting",
|
|
||||||
b"understanding",
|
|
||||||
b"assets",
|
|
||||||
b"funds",
|
|
||||||
];
|
|
||||||
|
|
||||||
static FW_TAB: FwTab = FwTab {
|
|
||||||
dir: [
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x09, 0x10, 0x18, 0x4f, 0x57, 0x6c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00,
|
|
||||||
0x00, 0x99, 0x9f, 0xa8, 0xb7, 0xd0, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
],
|
|
||||||
dir_len_bits: [
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0040, 0x0080, 0x44c8, 0x0080, 0x0260, 0x0060, 0x0000, 0x0070, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0020, 0x0100, 0x0140, 0x08c0, 0x2000, 0x0000, 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
],
|
|
||||||
strs: [
|
|
||||||
0x00, 0x02, 0x2f, 0x00, 0x02, 0x3a, 0x00, 0x01, 0x00, 0x06, 0x73, 0x73, 0x65, 0x74, 0x73, 0x00,
|
|
||||||
0x07, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x00, 0x07, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x79, 0x03,
|
|
||||||
0x6f, 0x6d, 0x07, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
|
|
||||||
0x69, 0x6e, 0x67, 0x07, 0x72, 0x75, 0x63, 0x69, 0x61, 0x6c, 0x06, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
|
||||||
0x0e, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x07,
|
|
||||||
0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x00, 0x05, 0x6d, 0x61, 0x69, 0x6c, 0x09, 0x73, 0x73, 0x65,
|
|
||||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x06, 0x78, 0x70, 0x65, 0x72, 0x73, 0x00, 0x06, 0x72, 0x65, 0x65,
|
|
||||||
0x7a, 0x65, 0x05, 0x75, 0x6e, 0x64, 0x73, 0x00, 0x04, 0x61, 0x63, 0x6b, 0x06, 0x61, 0x63, 0x6b,
|
|
||||||
0x65, 0x72, 0x04, 0x69, 0x72, 0x65, 0x04, 0x74, 0x74, 0x70, 0x05, 0x74, 0x74, 0x70, 0x73, 0x00,
|
|
||||||
0x08, 0x61, 0x6e, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x00, 0x05, 0x75, 0x69, 0x63, 0x6b, 0x00, 0x08,
|
|
||||||
0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x00, 0x08, 0x63, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73,
|
|
||||||
0x06, 0x74, 0x6f, 0x6c, 0x65, 0x6e, 0x00, 0x06, 0x65, 0x63, 0x68, 0x69, 0x65, 0x07, 0x72, 0x61,
|
|
||||||
0x63, 0x69, 0x6e, 0x67, 0x0b, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
|
||||||
0x0d, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x06, 0x61,
|
|
||||||
0x6c, 0x6c, 0x65, 0x74, 0x07, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x08, 0x68, 0x61, 0x74, 0x73,
|
|
||||||
0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
static FW_PHF: phf::Set<&'static [u8]> = phf::phf_set! {
|
|
||||||
b"recovery",
|
|
||||||
b"techie",
|
|
||||||
b"http",
|
|
||||||
b"https",
|
|
||||||
b"digital",
|
|
||||||
b"hack",
|
|
||||||
b"::",
|
|
||||||
b"//",
|
|
||||||
b"com",
|
|
||||||
b"@",
|
|
||||||
b"crypto",
|
|
||||||
b"bitcoin",
|
|
||||||
b"wallet",
|
|
||||||
b"hacker",
|
|
||||||
b"welcome",
|
|
||||||
b"whatsapp",
|
|
||||||
b"email",
|
|
||||||
b"cryptocurrency",
|
|
||||||
b"stolen",
|
|
||||||
b"freeze",
|
|
||||||
b"quick",
|
|
||||||
b"crucial",
|
|
||||||
b"tracing",
|
|
||||||
b"scammers",
|
|
||||||
b"expers",
|
|
||||||
b"hire",
|
|
||||||
b"century",
|
|
||||||
b"transaction",
|
|
||||||
b"essential",
|
|
||||||
b"managing",
|
|
||||||
b"contact",
|
|
||||||
b"contacting",
|
|
||||||
b"understanding",
|
|
||||||
b"assets",
|
|
||||||
b"funds",
|
|
||||||
};
|
|
||||||
|
|
||||||
//static FW_TAB_DIR: [u8; 256] = [
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x09, 0x10, 0x18, 0x4f, 0x57, 0x6c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00,
|
|
||||||
// 0x00, 0x99, 0x9f, 0xa8, 0xb7, 0xd0, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
//];
|
|
||||||
//static FW_TAB_STRS: [u8; 244] = [
|
|
||||||
// 0x00, 0x02, 0x2f, 0x00, 0x02, 0x3a, 0x00, 0x01, 0x00, 0x06, 0x73, 0x73, 0x65, 0x74, 0x73, 0x00,
|
|
||||||
// 0x07, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x00, 0x07, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x79, 0x03,
|
|
||||||
// 0x6f, 0x6d, 0x07, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
|
|
||||||
// 0x69, 0x6e, 0x67, 0x07, 0x72, 0x75, 0x63, 0x69, 0x61, 0x6c, 0x06, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
|
||||||
// 0x0e, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x07,
|
|
||||||
// 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x00, 0x05, 0x6d, 0x61, 0x69, 0x6c, 0x09, 0x73, 0x73, 0x65,
|
|
||||||
// 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x06, 0x78, 0x70, 0x65, 0x72, 0x73, 0x00, 0x06, 0x72, 0x65, 0x65,
|
|
||||||
// 0x7a, 0x65, 0x05, 0x75, 0x6e, 0x64, 0x73, 0x00, 0x04, 0x61, 0x63, 0x6b, 0x06, 0x61, 0x63, 0x6b,
|
|
||||||
// 0x65, 0x72, 0x04, 0x69, 0x72, 0x65, 0x04, 0x74, 0x74, 0x70, 0x05, 0x74, 0x74, 0x70, 0x73, 0x00,
|
|
||||||
// 0x08, 0x61, 0x6e, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x00, 0x05, 0x75, 0x69, 0x63, 0x6b, 0x00, 0x08,
|
|
||||||
// 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x00, 0x08, 0x63, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73,
|
|
||||||
// 0x06, 0x74, 0x6f, 0x6c, 0x65, 0x6e, 0x00, 0x06, 0x65, 0x63, 0x68, 0x69, 0x65, 0x07, 0x72, 0x61,
|
|
||||||
// 0x63, 0x69, 0x6e, 0x67, 0x0b, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
|
||||||
// 0x0d, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x06, 0x61,
|
|
||||||
// 0x6c, 0x6c, 0x65, 0x74, 0x07, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x08, 0x68, 0x61, 0x74, 0x73,
|
|
||||||
// 0x61, 0x70, 0x70, 0x00,
|
|
||||||
//];
|
|
||||||
@@ -1,925 +0,0 @@
|
|||||||
#![feature(likely_unlikely)]
|
|
||||||
|
|
||||||
mod books;
|
|
||||||
|
|
||||||
use crate::books::FULL_BOOK_PATHS;
|
|
||||||
use core_affinity::CoreId;
|
|
||||||
use libc::{aio_read, aiocb};
|
|
||||||
use memmap2::Mmap;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::cell::OnceCell;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ffi::{OsStr, OsString};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::os::linux::raw::stat;
|
|
||||||
use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::thread::available_parallelism;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use std::{array, env, fs, hint, mem, process, thread};
|
|
||||||
use monoio::IoUringDriver;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_whitespace(b: u8) -> bool {
|
|
||||||
matches!(b, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_upper(b: u8) -> bool {
|
|
||||||
matches!(b, b'A'..=b'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_ascii_digit(b: u8) -> bool {
|
|
||||||
matches!(b, b'0'..=b'9')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(align(128))]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Stats {
|
|
||||||
pub sentences: u32,
|
|
||||||
pub words: u32,
|
|
||||||
pub capitalizeds: u32,
|
|
||||||
pub numbers: u32,
|
|
||||||
pub forbiddens: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
static TIME_SPENT_READING_FILES: Mutex<Duration> = Mutex::new(Duration::from_secs(0));
|
|
||||||
|
|
||||||
const TEMP_MEM_SIZE: usize = 6 * 1024 * 1024;
|
|
||||||
thread_local! {
|
|
||||||
static WORK_STATE: RefCell<WorkState> = RefCell::new(WorkState::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WorkState {
|
|
||||||
pub work_mem: Box<[u8]>,
|
|
||||||
pub empty_vec: Box<[u8]>,
|
|
||||||
// pub io_mem: Box<[u8]>,
|
|
||||||
// pub curr_read: Option<aiocb>,
|
|
||||||
// pub had_first_load: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkState {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
work_mem: vec![0; TEMP_MEM_SIZE].into_boxed_slice(),
|
|
||||||
empty_vec: vec![].into_boxed_slice(),
|
|
||||||
// io_mem: vec![0; TEMP_MEM_SIZE].into_boxed_slice(),
|
|
||||||
// curr_read: None,
|
|
||||||
// had_first_load: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn work(file_path: &OsStr, stats: &mut Stats) {
|
|
||||||
WORK_STATE.with_borrow_mut(|state: &mut WorkState| {
|
|
||||||
// // Load file
|
|
||||||
// let start_time = Instant::now();
|
|
||||||
|
|
||||||
// let Ok(text) = fs::read(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// process::abort();
|
|
||||||
// };
|
|
||||||
|
|
||||||
let mut file = File::open(file_path).unwrap();
|
|
||||||
let file_len = file.metadata().unwrap().len() as usize;
|
|
||||||
file.read_exact(&mut state.work_mem[..file_len]).unwrap();
|
|
||||||
let text = &state.work_mem[..file_len];
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut cb = mem::zeroed();
|
|
||||||
|
|
||||||
aio_read(&raw mut cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// let text = include_bytes!("../../../books/Advanced Techniques in Web Intelligence – Part II.txt").as_slice();
|
|
||||||
|
|
||||||
// let time_reading = start_time.elapsed();
|
|
||||||
// {
|
|
||||||
// let mut guard = TIME_SPENT_READING_FILES.lock().unwrap();
|
|
||||||
// *guard += time_reading;
|
|
||||||
// }
|
|
||||||
|
|
||||||
analyze(&text, stats);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn analyze(text: &[u8], stats: &mut Stats) {
|
|
||||||
// // NOTE: mmap is quite a bit slower
|
|
||||||
// // Load file
|
|
||||||
// let Ok(file) = File::open(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// std::process::abort();
|
|
||||||
// };
|
|
||||||
// let mmap = unsafe {
|
|
||||||
// Mmap::map(&file).unwrap()
|
|
||||||
// };
|
|
||||||
// mem::forget(file);
|
|
||||||
// let text = &*mmap;
|
|
||||||
|
|
||||||
// // Load file
|
|
||||||
// let start_time = Instant::now();
|
|
||||||
// let Ok(text) = fs::read(file_path) else {
|
|
||||||
// eprintln!("invalid file!");
|
|
||||||
// process::abort();
|
|
||||||
// };
|
|
||||||
// let time_reading = start_time.elapsed();
|
|
||||||
// {
|
|
||||||
// let mut guard = TIME_SPENT_READING_FILES.lock().unwrap();
|
|
||||||
// *guard += time_reading;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let mut sentences = 0;
|
|
||||||
let mut words = 0;
|
|
||||||
let mut capitalizeds = 0;
|
|
||||||
let mut numbers = 0;
|
|
||||||
let mut forbiddens = 0;
|
|
||||||
|
|
||||||
let mut idx = 0;
|
|
||||||
'full_loop: loop {
|
|
||||||
// TODO: Necessary for now
|
|
||||||
if idx >= text.len() {
|
|
||||||
break 'full_loop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip whitespace
|
|
||||||
while is_ascii_whitespace(text[idx]) {
|
|
||||||
idx += 1;
|
|
||||||
if idx >= text.len() {
|
|
||||||
break 'full_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find end of word
|
|
||||||
let word_start = idx;
|
|
||||||
let mut has_non_upper = false;
|
|
||||||
|
|
||||||
'find_word_end: while let b = text[idx] && !is_ascii_whitespace(b) {
|
|
||||||
idx += 1;
|
|
||||||
if idx >= text.len() {
|
|
||||||
break 'find_word_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-char logic
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
if !is_ascii_upper(b) {
|
|
||||||
has_non_upper = true;
|
|
||||||
}
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let word = &text[word_start..idx];
|
|
||||||
|
|
||||||
// dbg!(str::from_utf8(word).unwrap());
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
if !has_non_upper {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check forbidden
|
|
||||||
if unsafe { FW_TAB.lookup(word) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
for token in text.split(|&b| is_ascii_whitespace(b)) {
|
|
||||||
if token.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
// Sentence count, folded into this loop
|
|
||||||
// instead of another loop (better cache usage)
|
|
||||||
for &b in token {
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if upper
|
|
||||||
if token.iter().all(|&b| is_ascii_upper(b)) {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
for &b in token {
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if words
|
|
||||||
// if FORBIDDEN_WORDS.contains(&token) {
|
|
||||||
// if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, token) } {
|
|
||||||
if unsafe { FW_TAB.lookup(token) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// NOTE: This is pretty slow:
|
|
||||||
let mut idx = 0;
|
|
||||||
let mut word_start = 0;
|
|
||||||
let mut is_in_word = false;
|
|
||||||
let mut has_non_upper = false;
|
|
||||||
loop {
|
|
||||||
let b = unsafe { *text.get_unchecked(idx) };
|
|
||||||
|
|
||||||
let mut process_word = false;
|
|
||||||
if is_ascii_whitespace(b) {
|
|
||||||
if is_in_word {
|
|
||||||
process_word = true;
|
|
||||||
|
|
||||||
// Reset state for next word
|
|
||||||
is_in_word = false;
|
|
||||||
has_non_upper = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !is_in_word {
|
|
||||||
word_start = idx;
|
|
||||||
is_in_word = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
has_non_upper |= !is_ascii_upper(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
// Check sentences
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let word = &text[word_start..idx];
|
|
||||||
|
|
||||||
idx += 1;
|
|
||||||
|
|
||||||
if process_word || idx >= text.len() {
|
|
||||||
words += 1;
|
|
||||||
if !has_non_upper {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// // DEBUG:
|
|
||||||
// println!("'{}'", str::from_utf8(word).unwrap());
|
|
||||||
|
|
||||||
if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, word) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if idx >= text.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
stats.sentences = sentences;
|
|
||||||
stats.words = words;
|
|
||||||
stats.capitalizeds = capitalizeds;
|
|
||||||
stats.numbers = numbers;
|
|
||||||
stats.forbiddens = forbiddens;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn analyze_old(file_path: &OsStr, stats: &mut Stats) {
|
|
||||||
// Load file
|
|
||||||
let Ok(text) = fs::read(file_path) else {
|
|
||||||
eprintln!("invalid file!");
|
|
||||||
std::process::abort();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut sentences = 0;
|
|
||||||
let mut words = 0;
|
|
||||||
let mut capitalizeds = 0;
|
|
||||||
let mut numbers = 0;
|
|
||||||
let mut forbiddens = 0;
|
|
||||||
|
|
||||||
for token in text.split(|&b| is_ascii_whitespace(b)) {
|
|
||||||
if token.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
words += 1;
|
|
||||||
|
|
||||||
// Sentence count, folded into this loop
|
|
||||||
// instead of another loop (better cache usage)
|
|
||||||
for &b in token {
|
|
||||||
if b == b'.' {
|
|
||||||
sentences += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if upper
|
|
||||||
if token.iter().all(|&b| is_ascii_upper(b)) {
|
|
||||||
capitalizeds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digits
|
|
||||||
for &b in token {
|
|
||||||
if is_ascii_digit(b) {
|
|
||||||
numbers += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if words
|
|
||||||
// if FORBIDDEN_WORDS.contains(&token) {
|
|
||||||
if unsafe { FwTab::lookup_raw(&FW_TAB_DIR, &FW_TAB_STRS, token) } {
|
|
||||||
forbiddens += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.sentences = sentences;
|
|
||||||
stats.words = words;
|
|
||||||
stats.capitalizeds = capitalizeds;
|
|
||||||
stats.numbers = numbers;
|
|
||||||
stats.forbiddens = forbiddens;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Read in files from args
|
|
||||||
let mut files = Vec::with_capacity(env::args().len());
|
|
||||||
let mut do_parallel = false;
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
for arg in env::args_os().skip(1) {
|
|
||||||
// skip program arg
|
|
||||||
if arg == "-p" {
|
|
||||||
do_parallel = true;
|
|
||||||
} else {
|
|
||||||
files.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("[PROFILE] taking args took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
// env::args_os().
|
|
||||||
|
|
||||||
// let files = FULL_BOOK_PATHS;
|
|
||||||
|
|
||||||
// // Build table
|
|
||||||
// let tab = FwTab::build();
|
|
||||||
// tab.compile();
|
|
||||||
|
|
||||||
// Do the work
|
|
||||||
let mut stats = vec![Stats {
|
|
||||||
sentences: 0,
|
|
||||||
words: 0,
|
|
||||||
capitalizeds: 0,
|
|
||||||
numbers: 0,
|
|
||||||
forbiddens: 0,
|
|
||||||
}; files.len()];
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let num_cores = available_parallelism().unwrap().get();
|
|
||||||
let num_threads = num_cores * 1;
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
dbg!(num_threads);
|
|
||||||
dbg!(num_cores);
|
|
||||||
|
|
||||||
let next_file_idx = &*Box::leak(Box::new(AtomicUsize::new(0)));
|
|
||||||
|
|
||||||
thread::scope(|scope| {
|
|
||||||
for thread_idx in 0..num_threads {
|
|
||||||
// Set thread affinity
|
|
||||||
assert!(core_affinity::set_for_current(CoreId { id: thread_idx % num_cores }));
|
|
||||||
|
|
||||||
let cap_next_file_idx = &next_file_idx;
|
|
||||||
let cap_stats_ptr = stats.as_ptr() as usize;
|
|
||||||
let cap_files = &files;
|
|
||||||
thread::Builder::new().spawn_scoped(scope, move || {
|
|
||||||
let files = cap_files;
|
|
||||||
|
|
||||||
// let exec = glommio::LocalExecutorBuilder::new(Placement::Unbound).make().unwrap();
|
|
||||||
// exec.run(async {
|
|
||||||
// println!("Running in glommio thread {core_idx}");
|
|
||||||
// });
|
|
||||||
|
|
||||||
let mut rt = monoio::RuntimeBuilder::<IoUringDriver>::new()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut work_mem = vec![0; TEMP_MEM_SIZE].into_boxed_slice();
|
|
||||||
let files_per_thread = files.len() / num_threads;
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
// // Claim next file id
|
|
||||||
// loop {
|
|
||||||
// let work_idx = cap_next_file_idx.fetch_add(1, Ordering::Relaxed);
|
|
||||||
// if work_idx >= files.len() {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Do work
|
|
||||||
let thread_start = thread_idx * files_per_thread;
|
|
||||||
for i in 0..files_per_thread {
|
|
||||||
let work_idx = thread_start + i;
|
|
||||||
|
|
||||||
let path = &files[work_idx];
|
|
||||||
let stat = unsafe {
|
|
||||||
&mut *(cap_stats_ptr as *mut Stats)
|
|
||||||
.offset(work_idx as isize)
|
|
||||||
};
|
|
||||||
|
|
||||||
let file = monoio::fs::File::open(path)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
struct CappedReadBuf(pub Box<[u8]>, usize);
|
|
||||||
unsafe impl monoio::buf::IoBufMut for CappedReadBuf {
|
|
||||||
fn write_ptr(&mut self) -> *mut u8 {
|
|
||||||
monoio::buf::IoBufMut::write_ptr(&mut self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bytes_total(&mut self) -> usize {
|
|
||||||
self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn set_init(&mut self, pos: usize) {
|
|
||||||
monoio::buf::IoBufMut::set_init(&mut self.0, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let meta = file.metadata().await.unwrap();
|
|
||||||
let io_mem = mem::take(&mut work_mem);
|
|
||||||
let (res, buf) = file.read_exact_at(CappedReadBuf(io_mem, meta.len() as usize), 0).await;
|
|
||||||
res.unwrap();
|
|
||||||
work_mem = buf.0;
|
|
||||||
|
|
||||||
analyze(&work_mem, stat);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// exec.run(async {
|
|
||||||
// // Claim next file id
|
|
||||||
// let work_idx = cap_next_file_idx.fetch_add(1, Ordering::Relaxed);
|
|
||||||
//
|
|
||||||
// let path = &files[work_idx];
|
|
||||||
// let stat = unsafe {
|
|
||||||
// &mut *(stats_ptr as *mut Stats)
|
|
||||||
// .offset(work_idx as isize)
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// work(path, stat);
|
|
||||||
// });
|
|
||||||
}).unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// rayon::ThreadPoolBuilder::new()
|
|
||||||
// .num_threads(num_threads)
|
|
||||||
// .build_global()
|
|
||||||
// .unwrap();
|
|
||||||
//
|
|
||||||
// files.par_iter()
|
|
||||||
// .enumerate()
|
|
||||||
// .for_each(|(idx, p)| {
|
|
||||||
// let s = unsafe {
|
|
||||||
// &mut *stats.as_ptr()
|
|
||||||
// .offset(idx as isize)
|
|
||||||
// .cast_mut()
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
//// let mut path = OsString::from("../../");
|
|
||||||
//// path.push(p);
|
|
||||||
// let path = p;
|
|
||||||
// work(path, s);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// thread::scope(|scope| {
|
|
||||||
// let files_per_thread = files.len() / num_threads;
|
|
||||||
//
|
|
||||||
// for thread_idx in 0..num_threads {
|
|
||||||
// let capture_files = &files;
|
|
||||||
// let capture_stats = &stats;
|
|
||||||
// thread::Builder::new().spawn_scoped(scope, move || {
|
|
||||||
// let files = capture_files;
|
|
||||||
// let stats = capture_stats;
|
|
||||||
//
|
|
||||||
// // Set thread affinity
|
|
||||||
// assert!(core_affinity::set_for_current(CoreId { id: thread_idx % num_cores }));
|
|
||||||
//
|
|
||||||
// // Do work
|
|
||||||
// let thread_start = thread_idx * files_per_thread;
|
|
||||||
// for i in 0..files_per_thread {
|
|
||||||
// let real_idx = thread_start + i;
|
|
||||||
// let file_path = &files[real_idx];
|
|
||||||
// let st = unsafe {
|
|
||||||
// &mut *stats.as_ptr()
|
|
||||||
// .offset(real_idx as isize)
|
|
||||||
// .cast_mut()
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// work(&file_path, st);
|
|
||||||
// }
|
|
||||||
// }).unwrap();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
println!("[PROFILE] processing text took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
// Accumulate stats
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let mut total_words = 0;
|
|
||||||
let mut total_capitalizeds = 0;
|
|
||||||
let mut total_sentences = 0;
|
|
||||||
let mut total_numbers = 0;
|
|
||||||
let mut total_forbiddens = 0;
|
|
||||||
|
|
||||||
for stat in &stats {
|
|
||||||
total_words += stat.words;
|
|
||||||
total_capitalizeds += stat.capitalizeds;
|
|
||||||
total_sentences += stat.sentences;
|
|
||||||
total_numbers += stat.numbers;
|
|
||||||
total_forbiddens += stat.forbiddens;
|
|
||||||
}
|
|
||||||
|
|
||||||
let capitalized_percentage = (total_capitalizeds as f32 / total_words as f32) * 100.0;
|
|
||||||
let forbidden_percentage = (total_forbiddens as f32 / total_words as f32) * 100.0;
|
|
||||||
let word_count_per_sentence = total_words as f32 / total_sentences as f32;
|
|
||||||
|
|
||||||
println!();
|
|
||||||
println!("Total Words: {total_words}");
|
|
||||||
println!("Total Capitalized words: {total_capitalizeds}");
|
|
||||||
println!("Total Sentences: {total_sentences}");
|
|
||||||
println!("Total Numbers: {total_numbers}");
|
|
||||||
println!("Total Forbidden words: {total_forbiddens}");
|
|
||||||
println!("Capitalized percentage: {capitalized_percentage:.6}");
|
|
||||||
println!("Forbidden percentage: {forbidden_percentage:.6}");
|
|
||||||
println!("Word count per sentence: {word_count_per_sentence:.6}");
|
|
||||||
println!("Total files read: {}", files.len());
|
|
||||||
|
|
||||||
println!("[PROFILE] accumulating stats took {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
println!("[PROFILE] total file reading took {:?}", &*TIME_SPENT_READING_FILES.lock().unwrap());
|
|
||||||
|
|
||||||
// Exit process to avoid running drops
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct FwTab {
|
|
||||||
// pub dir_and_len_bits: [u32; 256],
|
|
||||||
pub dir_len_bits: [u16; 256],
|
|
||||||
pub dir: [u8; 256],
|
|
||||||
pub strs: [u8; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FwTab {
|
|
||||||
pub fn build() -> Self {
|
|
||||||
// Sort fws by first char
|
|
||||||
let mut sorted_fws: Vec<Vec<&'static [u8]>> = vec![vec![]; 256];
|
|
||||||
|
|
||||||
for word in FORBIDDEN_WORDS {
|
|
||||||
sorted_fws[word[0] as usize].push(&word);
|
|
||||||
}
|
|
||||||
for i in 0..256 {
|
|
||||||
sorted_fws[i].sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// // DEBUG:
|
|
||||||
// println!("{:#?}", sorted_fws[b'@' as usize].iter().map(|s| str::from_utf8(s).unwrap()).collect::<Vec<_>>());
|
|
||||||
|
|
||||||
// Build str tab
|
|
||||||
let mut fw_dir = [0u8; 256];
|
|
||||||
// let mut fw_dir_len_bits = [0u32; 256];
|
|
||||||
let mut fw_dir_len_bits = [0u16; 256];
|
|
||||||
let mut fw_strs: Vec<u8> = vec![];
|
|
||||||
|
|
||||||
fw_strs.push(b'\0'); // push dummy value so that 0 in the dir means no-entries
|
|
||||||
|
|
||||||
for c in 0..256 {
|
|
||||||
for fw in FORBIDDEN_WORDS {
|
|
||||||
if c == fw[0] as usize {
|
|
||||||
fw_dir_len_bits[c] |= 0x1 << fw.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sorted_fws[c].is_empty() {
|
|
||||||
let sublist_start_offset = fw_strs.len().try_into().unwrap();
|
|
||||||
fw_dir[c] = sublist_start_offset;
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
println!("{c} start offset: {}", sublist_start_offset);
|
|
||||||
println!("{:#?}", sorted_fws[c].iter().map(|s| str::from_utf8(s).unwrap()).collect::<Vec<_>>());
|
|
||||||
|
|
||||||
// Push strings
|
|
||||||
for fw in &sorted_fws[c] {
|
|
||||||
fw_strs.push(fw.len().try_into().unwrap());
|
|
||||||
for &c in &fw[1..] {
|
|
||||||
fw_strs.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark end of per-char word sublist
|
|
||||||
fw_strs.push(b'\0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEBUG:
|
|
||||||
println!("strs len: {}", fw_strs.len());
|
|
||||||
|
|
||||||
assert_eq!(fw_dir.len(), 256);
|
|
||||||
assert!(fw_strs.len() <= 256);
|
|
||||||
|
|
||||||
fw_strs.resize(256, 0);
|
|
||||||
|
|
||||||
let tab = FwTab {
|
|
||||||
dir: fw_dir,
|
|
||||||
dir_len_bits: fw_dir_len_bits,
|
|
||||||
// dir_and_len_bits: array::from_fn(|idx| {
|
|
||||||
// (fw_dir_len_bits[idx] & 0xff_ff_ff) | ((fw_dir[idx] as u32) << 24)
|
|
||||||
// }),
|
|
||||||
strs: fw_strs.try_into().unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// DEBUG: Test some strings
|
|
||||||
unsafe {
|
|
||||||
dbg!(tab.lookup(b"cpm"));
|
|
||||||
dbg!(tab.lookup(b"com"));
|
|
||||||
dbg!(tab.lookup(b"coma"));
|
|
||||||
dbg!(tab.lookup(b"co"));
|
|
||||||
dbg!(tab.lookup(b"cam"));
|
|
||||||
dbg!(tab.lookup(b"crypto"));
|
|
||||||
dbg!(tab.lookup(b"@"));
|
|
||||||
dbg!(tab.lookup(b""));
|
|
||||||
dbg!(tab.lookup(b" "));
|
|
||||||
dbg!(tab.lookup(b"test"));
|
|
||||||
dbg!(tab.lookup(b"expers"));
|
|
||||||
}
|
|
||||||
|
|
||||||
tab
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile(&self) {
|
|
||||||
println!("static FW_TAB: FwTab = FwTab {{");
|
|
||||||
|
|
||||||
// println!("\tdir_and_len_bits: [");
|
|
||||||
// for chunk in self.dir_and_len_bits.chunks(16) {
|
|
||||||
// print!("\t\t");
|
|
||||||
// for &b in chunk {
|
|
||||||
// print!("0x{b:08x}, ");
|
|
||||||
// }
|
|
||||||
// println!();
|
|
||||||
// }
|
|
||||||
// println!("\t],");
|
|
||||||
|
|
||||||
println!("\tdir: [");
|
|
||||||
for chunk in self.dir.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:02x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("\tdir_len_bits: [");
|
|
||||||
for chunk in self.dir_len_bits.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:04x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("\tstrs: [");
|
|
||||||
for chunk in self.strs.chunks(16) {
|
|
||||||
print!("\t\t");
|
|
||||||
for &b in chunk {
|
|
||||||
print!("0x{b:02x}, ");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
println!("\t],");
|
|
||||||
|
|
||||||
println!("}};");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn lookup(&self, word: &[u8]) -> bool {
|
|
||||||
let &[first_char, ..] = word else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// let dir_and_len_bits = unsafe {
|
|
||||||
// *self.dir_and_len_bits.get_unchecked(first_char as usize)
|
|
||||||
// };
|
|
||||||
// if word.len() < 23 && ((dir_and_len_bits >> word.len()) & 0x1) == 0 {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let len_bits = unsafe {
|
|
||||||
*self.dir_len_bits.get_unchecked(first_char as usize)
|
|
||||||
};
|
|
||||||
if word.len() < 16 && ((len_bits >> word.len()) & 0x1) == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let mut str_offset = (dir_and_len_bits >> 24) as usize;
|
|
||||||
let mut str_offset = unsafe {
|
|
||||||
*self.dir.get_unchecked(first_char as usize) as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
// Char doesn't have any strings in the table
|
|
||||||
if str_offset == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over strs
|
|
||||||
loop {
|
|
||||||
// let fw_len = u16::from_le_bytes([
|
|
||||||
// self.strs[str_offset],
|
|
||||||
// self.strs[str_offset+1]
|
|
||||||
// ]);
|
|
||||||
let fw_len: u8 = unsafe {
|
|
||||||
*self.strs.get_unchecked(str_offset)
|
|
||||||
};
|
|
||||||
|
|
||||||
if fw_len == 0 {
|
|
||||||
// We've reached the end of the word sublist
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only compare words if they are the same length
|
|
||||||
if word.len() == fw_len as usize {
|
|
||||||
// Compare strs
|
|
||||||
let mut char_offset = 1usize;
|
|
||||||
loop {
|
|
||||||
// Found the word!
|
|
||||||
if char_offset == word.len() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fw_char = unsafe { *self.strs.get_unchecked(str_offset + char_offset) };
|
|
||||||
let word_char = unsafe { *word.get_unchecked(char_offset) };
|
|
||||||
|
|
||||||
if fw_char > word_char {
|
|
||||||
// Word can't possible be in the sorted list, return
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if fw_char < word_char {
|
|
||||||
// Try next word
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
char_offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance to next word
|
|
||||||
// let str_len_bytes = 2;
|
|
||||||
let str_len_bytes = 1;
|
|
||||||
str_offset += (fw_len as usize - 1) + str_len_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const FORBIDDEN_WORDS: [&'static [u8]; 35] = [
|
|
||||||
b"recovery",
|
|
||||||
b"techie",
|
|
||||||
b"http",
|
|
||||||
b"https",
|
|
||||||
b"digital",
|
|
||||||
b"hack",
|
|
||||||
b"::",
|
|
||||||
b"//",
|
|
||||||
b"com",
|
|
||||||
b"@",
|
|
||||||
b"crypto",
|
|
||||||
b"bitcoin",
|
|
||||||
b"wallet",
|
|
||||||
b"hacker",
|
|
||||||
b"welcome",
|
|
||||||
b"whatsapp",
|
|
||||||
b"email",
|
|
||||||
b"cryptocurrency",
|
|
||||||
b"stolen",
|
|
||||||
b"freeze",
|
|
||||||
b"quick",
|
|
||||||
b"crucial",
|
|
||||||
b"tracing",
|
|
||||||
b"scammers",
|
|
||||||
b"expers",
|
|
||||||
b"hire",
|
|
||||||
b"century",
|
|
||||||
b"transaction",
|
|
||||||
b"essential",
|
|
||||||
b"managing",
|
|
||||||
b"contact",
|
|
||||||
b"contacting",
|
|
||||||
b"understanding",
|
|
||||||
b"assets",
|
|
||||||
b"funds",
|
|
||||||
];
|
|
||||||
|
|
||||||
static FW_TAB: FwTab = FwTab {
|
|
||||||
dir: [
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x09, 0x10, 0x18, 0x4f, 0x57, 0x6c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00,
|
|
||||||
0x00, 0x99, 0x9f, 0xa8, 0xb7, 0xd0, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
],
|
|
||||||
dir_len_bits: [
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0040, 0x0080, 0x44c8, 0x0080, 0x0260, 0x0060, 0x0000, 0x0070, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0020, 0x0100, 0x0140, 0x08c0, 0x2000, 0x0000, 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
],
|
|
||||||
strs: [
|
|
||||||
0x00, 0x02, 0x2f, 0x00, 0x02, 0x3a, 0x00, 0x01, 0x00, 0x06, 0x73, 0x73, 0x65, 0x74, 0x73, 0x00,
|
|
||||||
0x07, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x00, 0x07, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x79, 0x03,
|
|
||||||
0x6f, 0x6d, 0x07, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
|
|
||||||
0x69, 0x6e, 0x67, 0x07, 0x72, 0x75, 0x63, 0x69, 0x61, 0x6c, 0x06, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
|
||||||
0x0e, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x07,
|
|
||||||
0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x00, 0x05, 0x6d, 0x61, 0x69, 0x6c, 0x09, 0x73, 0x73, 0x65,
|
|
||||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x06, 0x78, 0x70, 0x65, 0x72, 0x73, 0x00, 0x06, 0x72, 0x65, 0x65,
|
|
||||||
0x7a, 0x65, 0x05, 0x75, 0x6e, 0x64, 0x73, 0x00, 0x04, 0x61, 0x63, 0x6b, 0x06, 0x61, 0x63, 0x6b,
|
|
||||||
0x65, 0x72, 0x04, 0x69, 0x72, 0x65, 0x04, 0x74, 0x74, 0x70, 0x05, 0x74, 0x74, 0x70, 0x73, 0x00,
|
|
||||||
0x08, 0x61, 0x6e, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x00, 0x05, 0x75, 0x69, 0x63, 0x6b, 0x00, 0x08,
|
|
||||||
0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x00, 0x08, 0x63, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73,
|
|
||||||
0x06, 0x74, 0x6f, 0x6c, 0x65, 0x6e, 0x00, 0x06, 0x65, 0x63, 0x68, 0x69, 0x65, 0x07, 0x72, 0x61,
|
|
||||||
0x63, 0x69, 0x6e, 0x67, 0x0b, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
|
||||||
0x0d, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x06, 0x61,
|
|
||||||
0x6c, 0x6c, 0x65, 0x74, 0x07, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x08, 0x68, 0x61, 0x74, 0x73,
|
|
||||||
0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
//static FW_TAB_DIR: [u8; 256] = [
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x09, 0x10, 0x18, 0x4f, 0x57, 0x6c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00,
|
|
||||||
// 0x00, 0x99, 0x9f, 0xa8, 0xb7, 0xd0, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
//];
|
|
||||||
//static FW_TAB_STRS: [u8; 244] = [
|
|
||||||
// 0x00, 0x02, 0x2f, 0x00, 0x02, 0x3a, 0x00, 0x01, 0x00, 0x06, 0x73, 0x73, 0x65, 0x74, 0x73, 0x00,
|
|
||||||
// 0x07, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x00, 0x07, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x79, 0x03,
|
|
||||||
// 0x6f, 0x6d, 0x07, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
|
|
||||||
// 0x69, 0x6e, 0x67, 0x07, 0x72, 0x75, 0x63, 0x69, 0x61, 0x6c, 0x06, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
|
||||||
// 0x0e, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x07,
|
|
||||||
// 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x00, 0x05, 0x6d, 0x61, 0x69, 0x6c, 0x09, 0x73, 0x73, 0x65,
|
|
||||||
// 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x06, 0x78, 0x70, 0x65, 0x72, 0x73, 0x00, 0x06, 0x72, 0x65, 0x65,
|
|
||||||
// 0x7a, 0x65, 0x05, 0x75, 0x6e, 0x64, 0x73, 0x00, 0x04, 0x61, 0x63, 0x6b, 0x06, 0x61, 0x63, 0x6b,
|
|
||||||
// 0x65, 0x72, 0x04, 0x69, 0x72, 0x65, 0x04, 0x74, 0x74, 0x70, 0x05, 0x74, 0x74, 0x70, 0x73, 0x00,
|
|
||||||
// 0x08, 0x61, 0x6e, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x00, 0x05, 0x75, 0x69, 0x63, 0x6b, 0x00, 0x08,
|
|
||||||
// 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x00, 0x08, 0x63, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73,
|
|
||||||
// 0x06, 0x74, 0x6f, 0x6c, 0x65, 0x6e, 0x00, 0x06, 0x65, 0x63, 0x68, 0x69, 0x65, 0x07, 0x72, 0x61,
|
|
||||||
// 0x63, 0x69, 0x6e, 0x67, 0x0b, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
|
||||||
// 0x0d, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x06, 0x61,
|
|
||||||
// 0x6c, 0x6c, 0x65, 0x74, 0x07, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x08, 0x68, 0x61, 0x74, 0x73,
|
|
||||||
// 0x61, 0x70, 0x70, 0x00,
|
|
||||||
//];
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
pub fn test() {
|
|
||||||
// let ring = io_uring::Builder::<io_uring::squeue::Entry, io_uring::cqueue::Entry>::default()
|
|
||||||
// .build(128)
|
|
||||||
// .unwrap();
|
|
||||||
//
|
|
||||||
// ring.
|
|
||||||
}
|
|
||||||
@@ -1,66 +1,15 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Ofast -march=native -mtune=native -flto=auto -fuse-linker-plugin \
|
CFLAGS = -Wall -Werror -Wextra -Ofast -std=c2x
|
||||||
-funroll-all-loops -finline-functions -finline-limit=10000 \
|
|
||||||
-fprefetch-loop-arrays -ftracer -fmodulo-sched \
|
|
||||||
-fmodulo-sched-allow-regmoves -fgcse-sm -fgcse-las \
|
|
||||||
-ftree-loop-distribution -ftree-loop-im -ftree-loop-ivcanon \
|
|
||||||
-fivopts -fvariable-expansion-in-unroller -fvect-cost-model=unlimited \
|
|
||||||
-fipa-pta -fipa-cp-clone -fdevirtualize-speculatively \
|
|
||||||
-fno-plt -fno-semantic-interposition -fomit-frame-pointer \
|
|
||||||
-fmerge-all-constants -fno-stack-protector -DNDEBUG \
|
|
||||||
-fno-trapping-math -fno-signed-zeros -freciprocal-math \
|
|
||||||
-mavx2 -mfma -mbmi2 -mlzcnt -mpopcnt \
|
|
||||||
-pthread
|
|
||||||
|
|
||||||
all: build run valgrind build_risspam run_risspam build_cpp build_borded_cpp build_py build_jest
|
all: build run
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@echo "Building retoor_c with PGO..."
|
@# removed -pedantic flag because it doesn't accept ' for formatting numbers
|
||||||
@rm -rf pgo_data && mkdir -p pgo_data
|
@# using printf
|
||||||
@$(CC) $(CFLAGS) -fprofile-dir=pgo_data -fprofile-generate retoor_c/isspam.c -o isspam
|
@$(CC) $(CFLAGS) isspam.c -o isspam
|
||||||
@./isspam ./spam/*.txt > /dev/null 2>&1 || true
|
|
||||||
@./isspam ./not_spam/*.txt > /dev/null 2>&1 || true
|
|
||||||
@[ -d books ] && ./isspam ./books/*.txt > /dev/null 2>&1 || true
|
|
||||||
@$(CC) $(CFLAGS) -fprofile-dir=pgo_data -fprofile-use -fprofile-correction retoor_c/isspam.c -o isspam
|
|
||||||
@rm -rf pgo_data
|
|
||||||
|
|
||||||
build_quick:
|
|
||||||
@echo "Compiling retoor_c project (no PGO)."
|
|
||||||
@$(CC) $(CFLAGS) retoor_c/isspam.c -o isspam
|
|
||||||
|
|
||||||
build_py:
|
|
||||||
@echo "Copying py file"
|
|
||||||
@cp retoor_c/isspam.py isspam.py
|
|
||||||
|
|
||||||
build_cpp:
|
|
||||||
@echo "Compiling C++ version of isspam."
|
|
||||||
@g++ -Ofast retoor_c/isspam.cpp -o isspam_cpp
|
|
||||||
|
|
||||||
build_borded_cpp:
|
|
||||||
@echo "Compiling Borded C++ version of isspam."
|
|
||||||
@g++ -std=c++23 -Ofast borded_cpp/src/main3.cpp -o borded_cpp_exec
|
|
||||||
|
|
||||||
build_risspam:
|
|
||||||
@echo "Compiling 12bitfloat_risspam project."
|
|
||||||
cd 12bitfloat_rust/risspam && cargo build --release && cp target/release/risspam ../../
|
|
||||||
|
|
||||||
build_jest:
|
|
||||||
@echo "compiling jest_rust project"
|
|
||||||
cd jest_rust && cargo build --release && cp target/release/jisspam ..
|
|
||||||
|
|
||||||
build_swift:
|
|
||||||
@echo "Compiling Swift version of isspam."
|
|
||||||
cd swift_isspam && swift build -c release && cp .build/release/sisspam ../
|
|
||||||
|
|
||||||
build_all: build build_py build_cpp build_borded_cpp build_risspam build_jest build_swift
|
|
||||||
|
|
||||||
run: run_spam wl run_not_spam
|
run: run_spam wl run_not_spam
|
||||||
run_risspam: run_spam_risspam run_not_spam_risspam
|
|
||||||
|
|
||||||
bench_rust: build_risspam benchmark_only
|
|
||||||
bench_rust_only: build_risspam
|
|
||||||
cd 12bitfloat_rust/risspam && time ./target/release/risspam ../../books/*.txt
|
|
||||||
|
|
||||||
format:
|
format:
|
||||||
clang-format *.c *.h -i
|
clang-format *.c *.h -i
|
||||||
|
|
||||||
@@ -73,32 +22,5 @@ run_spam:
|
|||||||
run_not_spam:
|
run_not_spam:
|
||||||
@./isspam ./not_spam/*.txt
|
@./isspam ./not_spam/*.txt
|
||||||
|
|
||||||
run_spam_risspam:
|
|
||||||
@./risspam ./spam/*.txt
|
|
||||||
|
|
||||||
run_not_spam_risspam:
|
|
||||||
@./risspam ./not_spam/*.txt
|
|
||||||
|
|
||||||
valgrind: build
|
valgrind: build
|
||||||
valgrind ./isspam ./spam/*.txt
|
valgrind ./isspam ./spam/*.txt
|
||||||
|
|
||||||
publish:
|
|
||||||
@wget https://retoor.molodetz.nl/api/packages/retoor/generic/env.py/1.0.0/env.py --quiet
|
|
||||||
@wget https://retoor.molodetz.nl/api/packages/retoor/generic/publish/1.0.0/publish --quiet
|
|
||||||
@chmod +x publish
|
|
||||||
@./publish isspam
|
|
||||||
@./publish risspam
|
|
||||||
@./publish sisspam
|
|
||||||
@rm publish
|
|
||||||
@rm env.py
|
|
||||||
|
|
||||||
benchmark:
|
|
||||||
-@rm -rf books
|
|
||||||
@echo "Extracting books."
|
|
||||||
@tar -xzf books.tar.gz books/
|
|
||||||
@echo "Extracted books."
|
|
||||||
@python bench.py
|
|
||||||
|
|
||||||
# Skip extracting books over and over
|
|
||||||
benchmark_only:
|
|
||||||
@python bench.py
|
|
||||||
@@ -1,50 +1,26 @@
|
|||||||
# isspam
|
# Isspam
|
||||||
Fast as light evaluator for text files to summarize specific details about the text files.
|
Fast as light evaluator for text files to summarize specific details about the text files.
|
||||||
|
|
||||||
This repository contains multiple versions of the same(-ish) algorithm.
|
|
||||||
|
|
||||||
## Versions
|
|
||||||
- C (isspam) written by **@retoor**
|
|
||||||
- Rust (risspam) written by **@12bitfloat**
|
|
||||||
- C++ (isspam_cpp) written by **@BordedDev**
|
|
||||||
- Rust (jisspam) written by **@jestdotty**
|
|
||||||
- Swift (sisspam) written by **@retoor**
|
|
||||||
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
Build all versions to the repo root:
|
|
||||||
```
|
```
|
||||||
make build_all
|
make build
|
||||||
```
|
```
|
||||||
|
Build with memory check (requires valgrind to be installed):
|
||||||
Build isspam (C) with memory check (requires valgrind to be installed):
|
|
||||||
```
|
```
|
||||||
make valgrind
|
make valgrind
|
||||||
```
|
```
|
||||||
|
|
||||||
## Benchmarking
|
|
||||||
After all binaries have been build to the repo root, you can benchmark them like this:
|
|
||||||
```
|
|
||||||
make benchmark
|
|
||||||
```
|
|
||||||
or without extracting books again:
|
|
||||||
```
|
|
||||||
make benchmark_only
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
### Using files as parameter
|
### Using files as parameter
|
||||||
```
|
```
|
||||||
./(r)isspam ./spam/*.txt
|
./isspam ./spam/*.txt
|
||||||
./(r)isspam ./not_spam/*.txt
|
./isspam ./not_spam/*.txt
|
||||||
```
|
```
|
||||||
### Using stdin
|
### Using stdin
|
||||||
Useful for automation. Works only on the isspam version.
|
Useful for automation.
|
||||||
```
|
```
|
||||||
cat ./spam/example_spam1.txt | ./isspam
|
cat ./spam/example_spam1.txt | ./isspam
|
||||||
```
|
```
|
||||||
## Example output
|
## Example output
|
||||||
Output example made by isspam.
|
|
||||||
```
|
```
|
||||||
File: ./spam/example_spam3.txt
|
File: ./spam/example_spam3.txt
|
||||||
Capitalized words: 39
|
Capitalized words: 39
|
||||||
@@ -71,12 +47,7 @@ Word count per sentence: 21
|
|||||||
Memory usage: 1 MB, 6.460 (re)allocated, 4.222 unqiue free'd, 0 in use.
|
Memory usage: 1 MB, 6.460 (re)allocated, 4.222 unqiue free'd, 0 in use.
|
||||||
```
|
```
|
||||||
## Valgrind status
|
## Valgrind status
|
||||||
Valgrind output for isspam version.
|
|
||||||
|
|
||||||
Rust variant thinks it's too cool for memory checks afterwards.
|
|
||||||
|
|
||||||
Date: 2024-11-30
|
Date: 2024-11-30
|
||||||
|
|
||||||
```
|
```
|
||||||
==58062==
|
==58062==
|
||||||
==58062== HEAP SUMMARY:
|
==58062== HEAP SUMMARY:
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
import time
|
|
||||||
|
|
||||||
print("***benchmarking***")
|
|
||||||
time_start = time.time()
|
|
||||||
subprocess.check_output('./isspam books/*.txt', shell=True)
|
|
||||||
print("Time C:",time.time() - time_start)
|
|
||||||
time_start = time.time()
|
|
||||||
subprocess.check_output('./risspam -p books/*.txt', shell=True)
|
|
||||||
print("Time Rust:",time.time() - time_start)
|
|
||||||
time_start = time.time()
|
|
||||||
subprocess.check_output('./isspam_cpp books/*.txt', shell=True)
|
|
||||||
print("Time CPP:",time.time() - time_start)
|
|
||||||
time_start = time.time()
|
|
||||||
subprocess.check_output('./borded_cpp_exec books/*.txt', shell=True)
|
|
||||||
print("Time Borded CPP:",time.time() - time_start)
|
|
||||||
time_start = time.time()
|
|
||||||
subprocess.check_output('./jisspam books/*.txt', shell=True)
|
|
||||||
print("Time Jest Rust:", time.time() - time_start)
|
|
||||||
time_start = time.time()
|
|
||||||
subprocess.check_output('./sisspam books/*.txt', shell=True)
|
|
||||||
print("Time Swift:", time.time() - time_start)
|
|
||||||
print("***end benchmark***")
|
|
||||||
Binary file not shown.
@@ -1,97 +0,0 @@
|
|||||||
*.d
|
|
||||||
*.slo
|
|
||||||
*.lo
|
|
||||||
*.o
|
|
||||||
*.obj
|
|
||||||
*.gch
|
|
||||||
*.pch
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
*.dll
|
|
||||||
*.mod
|
|
||||||
*.smod
|
|
||||||
*.lai
|
|
||||||
*.la
|
|
||||||
*.a
|
|
||||||
*.lib
|
|
||||||
*.exe
|
|
||||||
*.out
|
|
||||||
*.app
|
|
||||||
.idea/**/workspace.xml
|
|
||||||
.idea/**/tasks.xml
|
|
||||||
.idea/**/usage.statistics.xml
|
|
||||||
.idea/**/dictionaries
|
|
||||||
.idea/**/shelf
|
|
||||||
.idea/**/aws.xml
|
|
||||||
.idea/**/contentModel.xml
|
|
||||||
.idea/**/dataSources/
|
|
||||||
.idea/**/dataSources.ids
|
|
||||||
.idea/**/dataSources.local.xml
|
|
||||||
.idea/**/sqlDataSources.xml
|
|
||||||
.idea/**/dynamic.xml
|
|
||||||
.idea/**/uiDesigner.xml
|
|
||||||
.idea/**/dbnavigator.xml
|
|
||||||
.idea/**/gradle.xml
|
|
||||||
.idea/**/libraries
|
|
||||||
.idea
|
|
||||||
cmake-build-*/
|
|
||||||
.idea/**/mongoSettings.xml
|
|
||||||
*.iws
|
|
||||||
out/
|
|
||||||
.idea_modules/
|
|
||||||
atlassian-ide-plugin.xml
|
|
||||||
.idea/replstate.xml
|
|
||||||
.idea/sonarlint/
|
|
||||||
com_crashlytics_export_strings.xml
|
|
||||||
crashlytics.properties
|
|
||||||
crashlytics-build.properties
|
|
||||||
fabric.properties
|
|
||||||
.idea/httpRequests
|
|
||||||
.idea/caches/build_file_checksums.ser
|
|
||||||
*~
|
|
||||||
.fuse_hidden*
|
|
||||||
.directory
|
|
||||||
.Trash-*
|
|
||||||
.nfs*
|
|
||||||
CMakeLists.txt.user
|
|
||||||
CMakeCache.txt
|
|
||||||
CMakeFiles
|
|
||||||
CMakeScripts
|
|
||||||
Testing
|
|
||||||
Makefile
|
|
||||||
cmake_install.cmake
|
|
||||||
install_manifest.txt
|
|
||||||
compile_commands.json
|
|
||||||
CTestTestfile.cmake
|
|
||||||
_deps
|
|
||||||
CMakeUserPresets.json
|
|
||||||
Thumbs.db
|
|
||||||
Thumbs.db:encryptable
|
|
||||||
ehthumbs.db
|
|
||||||
ehthumbs_vista.db
|
|
||||||
*.stackdump
|
|
||||||
[Dd]esktop.ini
|
|
||||||
$RECYCLE.BIN/
|
|
||||||
*.cab
|
|
||||||
*.msi
|
|
||||||
*.msix
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
*.lnk
|
|
||||||
.DS_Store
|
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
Icon
|
|
||||||
._*
|
|
||||||
.DocumentRevisions-V100
|
|
||||||
.fseventsd
|
|
||||||
.Spotlight-V100
|
|
||||||
.TemporaryItems
|
|
||||||
.Trashes
|
|
||||||
.VolumeIcon.icns
|
|
||||||
.com.apple.timemachine.donotpresent
|
|
||||||
.AppleDB
|
|
||||||
.AppleDesktop
|
|
||||||
Network Trash Folder
|
|
||||||
Temporary Items
|
|
||||||
.apdisk
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.25)
|
|
||||||
project(isspam)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 26)
|
|
||||||
if (MSVC)
|
|
||||||
add_compile_options(/W4)
|
|
||||||
add_compile_options(/WX)
|
|
||||||
add_compile_options(/external:anglebrackets)
|
|
||||||
add_compile_options(/external:W0)
|
|
||||||
add_compile_options(/wd4100)
|
|
||||||
add_compile_options(/wd5050)
|
|
||||||
add_definitions(-DWIN32_LEAN_AND_MEAN -DVC_EXTRALEAN)
|
|
||||||
add_compile_definitions(WIN32_LEAN_AND_MEAN NOMINMAX)
|
|
||||||
else ()
|
|
||||||
add_compile_options(-Wall)
|
|
||||||
add_compile_options(-Wextra)
|
|
||||||
add_compile_options(-Wpedantic)
|
|
||||||
# add_compile_options(-Werror)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} src/main.cpp)
|
|
||||||
add_executable(${PROJECT_NAME}3 src/main3.cpp)
|
|
||||||
|
|
||||||
if (LINUX)
|
|
||||||
target_link_libraries(${PROJECT_NAME} tbb)
|
|
||||||
target_link_libraries(${PROJECT_NAME}3 tbb)
|
|
||||||
endif ()
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
FROM gcc:latest
|
|
||||||
RUN apt update && apt install -y cmake gdb
|
|
||||||
WORKDIR /home
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
services:
|
|
||||||
cpp:
|
|
||||||
build: .
|
|
||||||
command: ["sh","doit.sh"]
|
|
||||||
tty: true
|
|
||||||
stdin_open: true
|
|
||||||
volumes:
|
|
||||||
- ./:/home
|
|
||||||
- ../books:/books
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
rm -rf build | true
|
|
||||||
mkdir build && cd build && cmake .. && make
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <fstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <numeric>
|
|
||||||
#include <execution>
|
|
||||||
#include <format>
|
|
||||||
#include <codecvt>
|
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_print
|
|
||||||
#include <print>
|
|
||||||
#else
|
|
||||||
namespace std {
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
inline void print(T format, Args &&... args) {
|
|
||||||
auto f = std::vformat(format, std::make_format_args(args...));
|
|
||||||
std::cout << f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
inline void println(T format, Args &&... args) {
|
|
||||||
auto f = std::vformat(format, std::make_format_args(args...));
|
|
||||||
std::cout << f << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
constexpr std::array<std::wstring_view, 35> BAD_WORDS = {
|
|
||||||
L"recovery",
|
|
||||||
L"techie",
|
|
||||||
L"http",
|
|
||||||
L"https",
|
|
||||||
L"digital",
|
|
||||||
L"hack",
|
|
||||||
L"::",
|
|
||||||
L"//",
|
|
||||||
L"com",
|
|
||||||
L"@",
|
|
||||||
L"crypto",
|
|
||||||
L"bitcoin",
|
|
||||||
L"wallet",
|
|
||||||
L"hacker",
|
|
||||||
L"welcome",
|
|
||||||
L"whatsapp",
|
|
||||||
L"email",
|
|
||||||
L"cryptocurrency",
|
|
||||||
L"stolen",
|
|
||||||
L"freeze",
|
|
||||||
L"quick",
|
|
||||||
L"crucial",
|
|
||||||
L"tracing",
|
|
||||||
L"scammers",
|
|
||||||
L"expers",
|
|
||||||
L"hire",
|
|
||||||
L"century",
|
|
||||||
L"transaction",
|
|
||||||
L"essential",
|
|
||||||
L"managing",
|
|
||||||
L"contact",
|
|
||||||
L"contacting",
|
|
||||||
L"understanding",
|
|
||||||
L"assets",
|
|
||||||
L"funds",
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr auto SHORTEST_BAD_WORD = std::ranges::fold_left(BAD_WORDS, std::numeric_limits<std::size_t>::max(),
|
|
||||||
[](std::size_t current, const std::wstring_view &word) {
|
|
||||||
return std::min(current, word.size());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
constexpr auto LONGEST_BAD_WORD = std::ranges::fold_left(BAD_WORDS, std::numeric_limits<std::size_t>::min(),
|
|
||||||
[](std::size_t current, const std::wstring_view &word) {
|
|
||||||
return std::max(current, word.size());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
struct AnalysisResult {
|
|
||||||
std::size_t totalWordCount = 0;
|
|
||||||
std::size_t totalCapitalizedCount = 0;
|
|
||||||
std::size_t totalSentenceCount = 0;
|
|
||||||
std::size_t totalNumberCount = 0;
|
|
||||||
std::size_t totalForbiddenCount = 0;
|
|
||||||
std::size_t fileCount = 1;
|
|
||||||
|
|
||||||
std::size_t failCount = 0;
|
|
||||||
|
|
||||||
operator std::string() const {
|
|
||||||
return std::format(
|
|
||||||
"Word Count: {}\nCapitalized Count: {}\nSentence Count: {}\nNumber Count: {}\nForbidden Count: {}\nFile Count: {}\nFail Count: {}",
|
|
||||||
totalWordCount, totalCapitalizedCount, totalSentenceCount, totalNumberCount, totalForbiddenCount, fileCount, failCount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend AnalysisResult operator+(const AnalysisResult &lhs, const AnalysisResult &rhs) {
|
|
||||||
return {
|
|
||||||
lhs.totalWordCount + rhs.totalWordCount,
|
|
||||||
lhs.totalCapitalizedCount + rhs.totalCapitalizedCount,
|
|
||||||
lhs.totalSentenceCount + rhs.totalSentenceCount,
|
|
||||||
lhs.totalNumberCount + rhs.totalNumberCount,
|
|
||||||
lhs.totalForbiddenCount + rhs.totalForbiddenCount,
|
|
||||||
lhs.fileCount + rhs.fileCount,
|
|
||||||
lhs.failCount + rhs.failCount
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
void check_word(std::wstring &word, std::size_t &forbiddenCount) {
|
|
||||||
if (word.size() < SHORTEST_BAD_WORD || word.size() > LONGEST_BAD_WORD) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::ranges::transform(word, word.begin(), ::towlower);
|
|
||||||
if (std::ranges::find(BAD_WORDS, word) != BAD_WORDS.end()) {
|
|
||||||
forbiddenCount++;
|
|
||||||
}
|
|
||||||
// if (std::ranges::find_if(BAD_WORDS, [&word](const std::wstring_view &badWord) {
|
|
||||||
// return word.contains(badWord);
|
|
||||||
// }
|
|
||||||
// ) != BAD_WORDS.end()) {
|
|
||||||
// forbiddenCount++;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
AnalysisResult parseFile(const std::string_view &filename) {
|
|
||||||
std::wifstream file;
|
|
||||||
|
|
||||||
// surpress warning of deprecation
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(suppress : 4996)
|
|
||||||
file.imbue(std::locale(std::locale(), new std::codecvt_utf8<wchar_t>));
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
||||||
file.open(std::string(filename));
|
|
||||||
if (!file.is_open()) {
|
|
||||||
std::println("File doesn't exist: {}", filename);
|
|
||||||
return { };
|
|
||||||
}
|
|
||||||
|
|
||||||
AnalysisResult result{ };
|
|
||||||
|
|
||||||
bool inWord = false;
|
|
||||||
bool isDigit = false;
|
|
||||||
wchar_t c;
|
|
||||||
|
|
||||||
std::wstring word;
|
|
||||||
while (file.get(c)) {
|
|
||||||
if (c == '.') {
|
|
||||||
result.totalSentenceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::isspace(c)) {
|
|
||||||
inWord = false;
|
|
||||||
isDigit = false;
|
|
||||||
|
|
||||||
if (!word.empty()) {
|
|
||||||
check_word(word, result.totalForbiddenCount);
|
|
||||||
word.clear();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
if (!inWord) {
|
|
||||||
result.totalWordCount++;
|
|
||||||
if (std::isupper(c)) {
|
|
||||||
result.totalCapitalizedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inWord = true;
|
|
||||||
|
|
||||||
if (std::isdigit(c) && !isDigit) {
|
|
||||||
result.totalNumberCount++;
|
|
||||||
isDigit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
word.push_back(c);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// std::cout << "File state: " << file.rdstate() << " EOF" << file.eof() << " Fail" << file.fail() << " Bad" << file.bad() << std::endl;
|
|
||||||
|
|
||||||
if (!word.empty()) {
|
|
||||||
check_word(word, result.totalForbiddenCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (file.fail() && !file.eof()) {
|
|
||||||
result.failCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(const int argc, char *argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
std::println("Usage: {} <file1> <file2> ... <fileN>", argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AnalysisResult result = std::transform_reduce(std::execution::par_unseq, std::next(argv), argv + argc,
|
|
||||||
AnalysisResult{.fileCount = 0},
|
|
||||||
std::plus{ },
|
|
||||||
parseFile
|
|
||||||
);
|
|
||||||
|
|
||||||
double capitalizedPercentage = (result.totalWordCount > 0)
|
|
||||||
? static_cast<double>(result.totalCapitalizedCount) / result.totalWordCount * 100.0
|
|
||||||
: 0;
|
|
||||||
double forbiddenPercentage = (result.totalWordCount > 0)
|
|
||||||
? static_cast<double>(result.totalForbiddenCount) / result.totalWordCount * 100.0
|
|
||||||
: 0;
|
|
||||||
double wordCountPerSentence = (result.totalSentenceCount > 0)
|
|
||||||
? static_cast<double>(result.totalWordCount) / result.totalSentenceCount
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
std::println("{}\nCapitalized Percentage: {}%\nForbidden Percentage: {}%\nWord Count Per Sentence: {}", std::string(result),
|
|
||||||
capitalizedPercentage, forbiddenPercentage, wordCountPerSentence
|
|
||||||
);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <fstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <numeric>
|
|
||||||
#include <execution>
|
|
||||||
#include <format>
|
|
||||||
#include <codecvt>
|
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_print
|
|
||||||
#include <print>
|
|
||||||
#else
|
|
||||||
namespace std {
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
inline void print(T format, Args &&... args) {
|
|
||||||
auto f = std::vformat(format, std::make_format_args(args...));
|
|
||||||
std::cout << f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
inline void println(T format, Args &&... args) {
|
|
||||||
auto f = std::vformat(format, std::make_format_args(args...));
|
|
||||||
std::cout << f << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
constexpr std::array<std::wstring_view, 35> BAD_WORDS = {
|
|
||||||
L"recovery",
|
|
||||||
L"techie",
|
|
||||||
L"http",
|
|
||||||
L"https",
|
|
||||||
L"digital",
|
|
||||||
L"hack",
|
|
||||||
L"::",
|
|
||||||
L"//",
|
|
||||||
L"com",
|
|
||||||
L"@",
|
|
||||||
L"crypto",
|
|
||||||
L"bitcoin",
|
|
||||||
L"wallet",
|
|
||||||
L"hacker",
|
|
||||||
L"welcome",
|
|
||||||
L"whatsapp",
|
|
||||||
L"email",
|
|
||||||
L"cryptocurrency",
|
|
||||||
L"stolen",
|
|
||||||
L"freeze",
|
|
||||||
L"quick",
|
|
||||||
L"crucial",
|
|
||||||
L"tracing",
|
|
||||||
L"scammers",
|
|
||||||
L"expers",
|
|
||||||
L"hire",
|
|
||||||
L"century",
|
|
||||||
L"transaction",
|
|
||||||
L"essential",
|
|
||||||
L"managing",
|
|
||||||
L"contact",
|
|
||||||
L"contacting",
|
|
||||||
L"understanding",
|
|
||||||
L"assets",
|
|
||||||
L"funds",
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr auto SHORTEST_BAD_WORD = std::ranges::fold_left(BAD_WORDS, std::numeric_limits<std::size_t>::max(),
|
|
||||||
[](std::size_t current, const std::wstring_view &word) {
|
|
||||||
return std::min(current, word.size());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
constexpr auto LONGEST_BAD_WORD = std::ranges::fold_left(BAD_WORDS, std::numeric_limits<std::size_t>::min(),
|
|
||||||
[](std::size_t current, const std::wstring_view &word) {
|
|
||||||
return std::max(current, word.size());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
std::size_t totalWordCount = 0;
|
|
||||||
std::size_t totalCapitalizedCount = 0;
|
|
||||||
std::size_t totalSentenceCount = 0;
|
|
||||||
std::size_t totalNumberCount = 0;
|
|
||||||
std::size_t totalForbiddenCount = 0;
|
|
||||||
std::size_t fileCount = 1;
|
|
||||||
|
|
||||||
std::size_t failCount = 0;
|
|
||||||
|
|
||||||
void check_word(std::wstring &word, std::size_t &forbiddenCount) {
|
|
||||||
if (word.size() < SHORTEST_BAD_WORD || word.size() > LONGEST_BAD_WORD) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::ranges::transform(word, word.begin(), ::towlower);
|
|
||||||
if (std::ranges::find(BAD_WORDS, word) != BAD_WORDS.end()) {
|
|
||||||
forbiddenCount++;
|
|
||||||
}
|
|
||||||
// if (std::ranges::find_if(BAD_WORDS, [&word](const std::wstring_view &badWord) {
|
|
||||||
// return word.contains(badWord);
|
|
||||||
// }
|
|
||||||
// ) != BAD_WORDS.end()) {
|
|
||||||
// forbiddenCount++;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseFile(const std::string_view &filename) {
|
|
||||||
std::wifstream file;
|
|
||||||
|
|
||||||
// surpress warning of deprecation
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(suppress : 4996)
|
|
||||||
file.imbue(std::locale(std::locale(), new std::codecvt_utf8<wchar_t>));
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
||||||
file.open(std::string(filename));
|
|
||||||
if (!file.is_open()) {
|
|
||||||
std::println("File doesn't exist: {}", filename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool inWord = false;
|
|
||||||
bool isDigit = false;
|
|
||||||
wchar_t c;
|
|
||||||
|
|
||||||
std::wstring word;
|
|
||||||
while (file.get(c)) {
|
|
||||||
if (c == '.') {
|
|
||||||
totalSentenceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::isspace(c)) {
|
|
||||||
inWord = false;
|
|
||||||
isDigit = false;
|
|
||||||
|
|
||||||
if (!word.empty()) {
|
|
||||||
check_word(word, totalForbiddenCount);
|
|
||||||
word.clear();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
if (!inWord) {
|
|
||||||
totalWordCount++;
|
|
||||||
if (std::isupper(c)) {
|
|
||||||
totalCapitalizedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inWord = true;
|
|
||||||
|
|
||||||
if (std::isdigit(c) && !isDigit) {
|
|
||||||
totalNumberCount++;
|
|
||||||
isDigit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
word.push_back(c);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// std::cout << "File state: " << file.rdstate() << " EOF" << file.eof() << " Fail" << file.fail() << " Bad" << file.bad() << std::endl;
|
|
||||||
|
|
||||||
if (!word.empty()) {
|
|
||||||
check_word(word, totalForbiddenCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (file.fail() && !file.eof()) {
|
|
||||||
failCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(const int argc, char *argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
std::println("Usage: {} <file1> <file2> ... <fileN>", argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::for_each(std::execution::par_unseq, std::next(argv), argv + argc, parseFile);
|
|
||||||
|
|
||||||
double capitalizedPercentage = (totalWordCount > 0)
|
|
||||||
? static_cast<double>(totalCapitalizedCount) / totalWordCount * 100.0
|
|
||||||
: 0;
|
|
||||||
double forbiddenPercentage = (totalWordCount > 0)
|
|
||||||
? static_cast<double>(totalForbiddenCount) / totalWordCount * 100.0
|
|
||||||
: 0;
|
|
||||||
double wordCountPerSentence = (totalSentenceCount > 0)
|
|
||||||
? static_cast<double>(totalWordCount) / totalSentenceCount
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
std::println(
|
|
||||||
"Word Count: {}\nCapitalized Count: {}\nSentence Count: {}\nNumber Count: {}\nForbidden Count: {}\nFile Count: {}\nFail Count: {}\nCapitalized Percentage: {}%\nForbidden Percentage: {}%\nWord Count Per Sentence: {}",
|
|
||||||
totalWordCount, totalCapitalizedCount, totalSentenceCount, totalNumberCount, totalForbiddenCount, fileCount, failCount,
|
|
||||||
capitalizedPercentage, forbiddenPercentage, wordCountPerSentence
|
|
||||||
);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,576 +0,0 @@
|
|||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <fstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <execution>
|
|
||||||
#include <format>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <aio.h>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <sys/signal.h>
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_print
|
|
||||||
#include <print>
|
|
||||||
#else
|
|
||||||
namespace std {
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
inline void print(T format, Args &&... args) {
|
|
||||||
auto f = std::vformat(format, std::make_format_args(args...));
|
|
||||||
std::cout << f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
inline void println(T format, Args &&... args) {
|
|
||||||
auto f = std::vformat(format, std::make_format_args(args...));
|
|
||||||
std::cout << f << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
constexpr std::array<std::string_view, 35> BAD_WORDS = {
|
|
||||||
"recovery",
|
|
||||||
"techie",
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
"digital",
|
|
||||||
"hack",
|
|
||||||
"::",
|
|
||||||
"//",
|
|
||||||
"com",
|
|
||||||
"@",
|
|
||||||
"crypto",
|
|
||||||
"bitcoin",
|
|
||||||
"wallet",
|
|
||||||
"hacker",
|
|
||||||
"welcome",
|
|
||||||
"whatsapp",
|
|
||||||
"email",
|
|
||||||
"cryptocurrency",
|
|
||||||
"stolen",
|
|
||||||
"freeze",
|
|
||||||
"quick",
|
|
||||||
"crucial",
|
|
||||||
"tracing",
|
|
||||||
"scammers",
|
|
||||||
"expers",
|
|
||||||
"hire",
|
|
||||||
"century",
|
|
||||||
"transaction",
|
|
||||||
"essential",
|
|
||||||
"managing",
|
|
||||||
"contact",
|
|
||||||
"contacting",
|
|
||||||
"understanding",
|
|
||||||
"assets",
|
|
||||||
"funds",
|
|
||||||
};
|
|
||||||
const std::unordered_set<std::string_view> BAD_WORDS_SET(BAD_WORDS.begin(), BAD_WORDS.end());
|
|
||||||
static constexpr unsigned int crc_table[256] = {
|
|
||||||
0x00000000,
|
|
||||||
0x77073096,
|
|
||||||
0xee0e612c,
|
|
||||||
0x990951ba,
|
|
||||||
0x076dc419,
|
|
||||||
0x706af48f,
|
|
||||||
0xe963a535,
|
|
||||||
0x9e6495a3,
|
|
||||||
0x0edb8832,
|
|
||||||
0x79dcb8a4,
|
|
||||||
0xe0d5e91e,
|
|
||||||
0x97d2d988,
|
|
||||||
0x09b64c2b,
|
|
||||||
0x7eb17cbd,
|
|
||||||
0xe7b82d07,
|
|
||||||
0x90bf1d91,
|
|
||||||
0x1db71064,
|
|
||||||
0x6ab020f2,
|
|
||||||
0xf3b97148,
|
|
||||||
0x84be41de,
|
|
||||||
0x1adad47d,
|
|
||||||
0x6ddde4eb,
|
|
||||||
0xf4d4b551,
|
|
||||||
0x83d385c7,
|
|
||||||
0x136c9856,
|
|
||||||
0x646ba8c0,
|
|
||||||
0xfd62f97a,
|
|
||||||
0x8a65c9ec,
|
|
||||||
0x14015c4f,
|
|
||||||
0x63066cd9,
|
|
||||||
0xfa0f3d63,
|
|
||||||
0x8d080df5,
|
|
||||||
0x3b6e20c8,
|
|
||||||
0x4c69105e,
|
|
||||||
0xd56041e4,
|
|
||||||
0xa2677172,
|
|
||||||
0x3c03e4d1,
|
|
||||||
0x4b04d447,
|
|
||||||
0xd20d85fd,
|
|
||||||
0xa50ab56b,
|
|
||||||
0x35b5a8fa,
|
|
||||||
0x42b2986c,
|
|
||||||
0xdbbbc9d6,
|
|
||||||
0xacbcf940,
|
|
||||||
0x32d86ce3,
|
|
||||||
0x45df5c75,
|
|
||||||
0xdcd60dcf,
|
|
||||||
0xabd13d59,
|
|
||||||
0x26d930ac,
|
|
||||||
0x51de003a,
|
|
||||||
0xc8d75180,
|
|
||||||
0xbfd06116,
|
|
||||||
0x21b4f4b5,
|
|
||||||
0x56b3c423,
|
|
||||||
0xcfba9599,
|
|
||||||
0xb8bda50f,
|
|
||||||
0x2802b89e,
|
|
||||||
0x5f058808,
|
|
||||||
0xc60cd9b2,
|
|
||||||
0xb10be924,
|
|
||||||
0x2f6f7c87,
|
|
||||||
0x58684c11,
|
|
||||||
0xc1611dab,
|
|
||||||
0xb6662d3d,
|
|
||||||
0x76dc4190,
|
|
||||||
0x01db7106,
|
|
||||||
0x98d220bc,
|
|
||||||
0xefd5102a,
|
|
||||||
0x71b18589,
|
|
||||||
0x06b6b51f,
|
|
||||||
0x9fbfe4a5,
|
|
||||||
0xe8b8d433,
|
|
||||||
0x7807c9a2,
|
|
||||||
0x0f00f934,
|
|
||||||
0x9609a88e,
|
|
||||||
0xe10e9818,
|
|
||||||
0x7f6a0dbb,
|
|
||||||
0x086d3d2d,
|
|
||||||
0x91646c97,
|
|
||||||
0xe6635c01,
|
|
||||||
0x6b6b51f4,
|
|
||||||
0x1c6c6162,
|
|
||||||
0x856530d8,
|
|
||||||
0xf262004e,
|
|
||||||
0x6c0695ed,
|
|
||||||
0x1b01a57b,
|
|
||||||
0x8208f4c1,
|
|
||||||
0xf50fc457,
|
|
||||||
0x65b0d9c6,
|
|
||||||
0x12b7e950,
|
|
||||||
0x8bbeb8ea,
|
|
||||||
0xfcb9887c,
|
|
||||||
0x62dd1ddf,
|
|
||||||
0x15da2d49,
|
|
||||||
0x8cd37cf3,
|
|
||||||
0xfbd44c65,
|
|
||||||
0x4db26158,
|
|
||||||
0x3ab551ce,
|
|
||||||
0xa3bc0074,
|
|
||||||
0xd4bb30e2,
|
|
||||||
0x4adfa541,
|
|
||||||
0x3dd895d7,
|
|
||||||
0xa4d1c46d,
|
|
||||||
0xd3d6f4fb,
|
|
||||||
0x4369e96a,
|
|
||||||
0x346ed9fc,
|
|
||||||
0xad678846,
|
|
||||||
0xda60b8d0,
|
|
||||||
0x44042d73,
|
|
||||||
0x33031de5,
|
|
||||||
0xaa0a4c5f,
|
|
||||||
0xdd0d7cc9,
|
|
||||||
0x5005713c,
|
|
||||||
0x270241aa,
|
|
||||||
0xbe0b1010,
|
|
||||||
0xc90c2086,
|
|
||||||
0x5768b525,
|
|
||||||
0x206f85b3,
|
|
||||||
0xb966d409,
|
|
||||||
0xce61e49f,
|
|
||||||
0x5edef90e,
|
|
||||||
0x29d9c998,
|
|
||||||
0xb0d09822,
|
|
||||||
0xc7d7a8b4,
|
|
||||||
0x59b33d17,
|
|
||||||
0x2eb40d81,
|
|
||||||
0xb7bd5c3b,
|
|
||||||
0xc0ba6cad,
|
|
||||||
0xedb88320,
|
|
||||||
0x9abfb3b6,
|
|
||||||
0x03b6e20c,
|
|
||||||
0x74b1d29a,
|
|
||||||
0xead54739,
|
|
||||||
0x9dd277af,
|
|
||||||
0x04db2615,
|
|
||||||
0x73dc1683,
|
|
||||||
0xe3630b12,
|
|
||||||
0x94643b84,
|
|
||||||
0x0d6d6a3e,
|
|
||||||
0x7a6a5aa8,
|
|
||||||
0xe40ecf0b,
|
|
||||||
0x9309ff9d,
|
|
||||||
0x0a00ae27,
|
|
||||||
0x7d079eb1,
|
|
||||||
0xf00f9344,
|
|
||||||
0x8708a3d2,
|
|
||||||
0x1e01f268,
|
|
||||||
0x6906c2fe,
|
|
||||||
0xf762575d,
|
|
||||||
0x806567cb,
|
|
||||||
0x196c3671,
|
|
||||||
0x6e6b06e7,
|
|
||||||
0xfed41b76,
|
|
||||||
0x89d32be0,
|
|
||||||
0x10da7a5a,
|
|
||||||
0x67dd4acc,
|
|
||||||
0xf9b9df6f,
|
|
||||||
0x8ebeeff9,
|
|
||||||
0x17b7be43,
|
|
||||||
0x60b08ed5,
|
|
||||||
0xd6d6a3e8,
|
|
||||||
0xa1d1937e,
|
|
||||||
0x38d8c2c4,
|
|
||||||
0x4fdff252,
|
|
||||||
0xd1bb67f1,
|
|
||||||
0xa6bc5767,
|
|
||||||
0x3fb506dd,
|
|
||||||
0x48b2364b,
|
|
||||||
0xd80d2bda,
|
|
||||||
0xaf0a1b4c,
|
|
||||||
0x36034af6,
|
|
||||||
0x41047a60,
|
|
||||||
0xdf60efc3,
|
|
||||||
0xa867df55,
|
|
||||||
0x316e8eef,
|
|
||||||
0x4669be79,
|
|
||||||
0xcb61b38c,
|
|
||||||
0xbc66831a,
|
|
||||||
0x256fd2a0,
|
|
||||||
0x5268e236,
|
|
||||||
0xcc0c7795,
|
|
||||||
0xbb0b4703,
|
|
||||||
0x220216b9,
|
|
||||||
0x5505262f,
|
|
||||||
0xc5ba3bbe,
|
|
||||||
0xb2bd0b28,
|
|
||||||
0x2bb45a92,
|
|
||||||
0x5cb36a04,
|
|
||||||
0xc2d7ffa7,
|
|
||||||
0xb5d0cf31,
|
|
||||||
0x2cd99e8b,
|
|
||||||
0x5bdeae1d,
|
|
||||||
0x9b64c2b0,
|
|
||||||
0xec63f226,
|
|
||||||
0x756aa39c,
|
|
||||||
0x026d930a,
|
|
||||||
0x9c0906a9,
|
|
||||||
0xeb0e363f,
|
|
||||||
0x72076785,
|
|
||||||
0x05005713,
|
|
||||||
0x95bf4a82,
|
|
||||||
0xe2b87a14,
|
|
||||||
0x7bb12bae,
|
|
||||||
0x0cb61b38,
|
|
||||||
0x92d28e9b,
|
|
||||||
0xe5d5be0d,
|
|
||||||
0x7cdcefb7,
|
|
||||||
0x0bdbdf21,
|
|
||||||
0x86d3d2d4,
|
|
||||||
0xf1d4e242,
|
|
||||||
0x68ddb3f8,
|
|
||||||
0x1fda836e,
|
|
||||||
0x81be16cd,
|
|
||||||
0xf6b9265b,
|
|
||||||
0x6fb077e1,
|
|
||||||
0x18b74777,
|
|
||||||
0x88085ae6,
|
|
||||||
0xff0f6a70,
|
|
||||||
0x66063bca,
|
|
||||||
0x11010b5c,
|
|
||||||
0x8f659eff,
|
|
||||||
0xf862ae69,
|
|
||||||
0x616bffd3,
|
|
||||||
0x166ccf45,
|
|
||||||
0xa00ae278,
|
|
||||||
0xd70dd2ee,
|
|
||||||
0x4e048354,
|
|
||||||
0x3903b3c2,
|
|
||||||
0xa7672661,
|
|
||||||
0xd06016f7,
|
|
||||||
0x4969474d,
|
|
||||||
0x3e6e77db,
|
|
||||||
0xaed16a4a,
|
|
||||||
0xd9d65adc,
|
|
||||||
0x40df0b66,
|
|
||||||
0x37d83bf0,
|
|
||||||
0xa9bcae53,
|
|
||||||
0xdebb9ec5,
|
|
||||||
0x47b2cf7f,
|
|
||||||
0x30b5ffe9,
|
|
||||||
0xbdbdf21c,
|
|
||||||
0xcabac28a,
|
|
||||||
0x53b39330,
|
|
||||||
0x24b4a3a6,
|
|
||||||
0xbad03605,
|
|
||||||
0xcdd70693,
|
|
||||||
0x54de5729,
|
|
||||||
0x23d967bf,
|
|
||||||
0xb3667a2e,
|
|
||||||
0xc4614ab8,
|
|
||||||
0x5d681b02,
|
|
||||||
0x2a6f2b94,
|
|
||||||
0xb40bbe37,
|
|
||||||
0xc30c8ea1,
|
|
||||||
0x5a05df1b,
|
|
||||||
0x2d02ef8d
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr uint32_t crc32(std::string_view str) {
|
|
||||||
uint32_t crc = 0xffffffff;
|
|
||||||
for (auto c : str)
|
|
||||||
crc = (crc >> 8) ^ crc_table[(crc ^ c) & 0xff];
|
|
||||||
return crc ^ 0xffffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr uint32_t crc32(char const *str, const size_t size) {
|
|
||||||
uint32_t crc = 0xffffffff;
|
|
||||||
for (size_t i = 0; i < size; ++i)
|
|
||||||
crc = (crc >> 8) ^ crc_table[(crc ^ str[i]) & 0xff];
|
|
||||||
return crc ^ 0xffffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::array<uint32_t, 35> BAD_WORDS_HASH = {
|
|
||||||
crc32("recovery"),
|
|
||||||
crc32("techie"),
|
|
||||||
crc32("http"),
|
|
||||||
crc32("https"),
|
|
||||||
crc32("digital"),
|
|
||||||
crc32("hack"),
|
|
||||||
crc32("::"),
|
|
||||||
crc32("//"),
|
|
||||||
crc32("com"),
|
|
||||||
crc32("@"),
|
|
||||||
crc32("crypto"),
|
|
||||||
crc32("bitcoin"),
|
|
||||||
crc32("wallet"),
|
|
||||||
crc32("hacker"),
|
|
||||||
crc32("welcome"),
|
|
||||||
crc32("whatsapp"),
|
|
||||||
crc32("email"),
|
|
||||||
crc32("cryptocurrency"),
|
|
||||||
crc32("stolen"),
|
|
||||||
crc32("freeze"),
|
|
||||||
crc32("quick"),
|
|
||||||
crc32("crucial"),
|
|
||||||
crc32("tracing"),
|
|
||||||
crc32("scammers"),
|
|
||||||
crc32("expers"),
|
|
||||||
crc32("hire"),
|
|
||||||
crc32("century"),
|
|
||||||
crc32("transaction"),
|
|
||||||
crc32("essential"),
|
|
||||||
crc32("managing"),
|
|
||||||
crc32("contact"),
|
|
||||||
crc32("contacting"),
|
|
||||||
crc32("understanding"),
|
|
||||||
crc32("assets"),
|
|
||||||
crc32("funds")
|
|
||||||
};
|
|
||||||
const std::unordered_set BAD_WORDS_STR(BAD_WORDS.begin(), BAD_WORDS.end());
|
|
||||||
|
|
||||||
constexpr auto SHORTEST_BAD_WORD = std::ranges::fold_left(BAD_WORDS, std::numeric_limits<std::size_t>::max(),
|
|
||||||
[](std::size_t current, const std::string_view &word) {
|
|
||||||
return std::min(current, word.size());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
constexpr auto LONGEST_BAD_WORD = std::ranges::fold_left(BAD_WORDS, std::numeric_limits<std::size_t>::min(),
|
|
||||||
[](std::size_t current, const std::string_view &word) {
|
|
||||||
return std::max(current, word.size());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
int totalWordCount = 0;
|
|
||||||
int totalCapitalizedCount = 0;
|
|
||||||
int totalSentenceCount = 0;
|
|
||||||
int totalNumberCount = 0;
|
|
||||||
int totalForbiddenCount = 0;
|
|
||||||
int fileCount = 1;
|
|
||||||
|
|
||||||
int failCount = 0;
|
|
||||||
|
|
||||||
|
|
||||||
int done = 0;
|
|
||||||
|
|
||||||
|
|
||||||
struct info {
|
|
||||||
std::string_view name;
|
|
||||||
aiocb *cb;
|
|
||||||
const std::chrono::time_point<std::chrono::steady_clock> start = std::chrono::steady_clock::now();
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr void check_word_simple(const char *word, const ssize_t size) {
|
|
||||||
if (size < SHORTEST_BAD_WORD || size > LONGEST_BAD_WORD) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if (BAD_WORDS_SET.contains(word)) {
|
|
||||||
// totalForbiddenCount++;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const auto hs = crc32(word, size);
|
|
||||||
|
|
||||||
for (int i = 0; i < BAD_WORDS_HASH.size(); ++i) {
|
|
||||||
if (BAD_WORDS_HASH[i] == hs) {
|
|
||||||
totalForbiddenCount++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_str(char *str, ssize_t size) {
|
|
||||||
int mark = -1;
|
|
||||||
|
|
||||||
int fileWords = 0;
|
|
||||||
for (int pos = 0; pos <= size; ++pos) {
|
|
||||||
char *c = str + pos;
|
|
||||||
|
|
||||||
if (*c == '.') {
|
|
||||||
totalSentenceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*c == ' ' || *c == '\n' || *c == '\r' || *c == '\t') {
|
|
||||||
if (mark != -1) {
|
|
||||||
check_word_simple(str + mark, pos - mark);
|
|
||||||
mark = -1;
|
|
||||||
}
|
|
||||||
} else if (mark == -1) {
|
|
||||||
++fileWords;
|
|
||||||
if (*c >= 'A' && *c <= 'Z') {
|
|
||||||
totalCapitalizedCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
mark = pos;
|
|
||||||
} else if (*c >= '0' && *c <= '9') {
|
|
||||||
totalNumberCount++;
|
|
||||||
for (; pos <= size; ++pos) {
|
|
||||||
c = str + pos;
|
|
||||||
if (*c == '.') {
|
|
||||||
totalSentenceCount++;
|
|
||||||
}
|
|
||||||
if (*c == ' ' || *c == '\n' || *c == '\r' || *c == '\t') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mark = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mark != -1) {
|
|
||||||
check_word_simple(str + mark, size - mark);
|
|
||||||
}
|
|
||||||
|
|
||||||
totalWordCount += fileWords;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void aio_completion_handler(sigval_t sigval) {
|
|
||||||
|
|
||||||
fileCount++;
|
|
||||||
info *data = (info *)sigval.sival_ptr;
|
|
||||||
auto req = data->cb;
|
|
||||||
// auto req = (struct aiocb *)sigval.sival_ptr;
|
|
||||||
/* Did the request complete? */
|
|
||||||
auto error = aio_error(req);
|
|
||||||
if (error == 0) {
|
|
||||||
|
|
||||||
/* Request completed successfully, get the return status */
|
|
||||||
// const auto start{std::chrono::steady_clock::now()};
|
|
||||||
// const std::chrono::duration<double> start_seconds{start - (data->start)};
|
|
||||||
// std::println("File started {} in {}", data->name, start_seconds.count());
|
|
||||||
read_str((char *)req->aio_buf, aio_return(req));
|
|
||||||
// const auto finish{std::chrono::steady_clock::now()};
|
|
||||||
// const std::chrono::duration<double> elapsed_seconds{finish - (data->start)};
|
|
||||||
// std::println("File read {} in {}", data->name, elapsed_seconds.count());
|
|
||||||
} else {
|
|
||||||
std::println("Error at aio_error ({}): ", error);
|
|
||||||
failCount++;
|
|
||||||
}
|
|
||||||
--done;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(const int argc, char *argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
std::println("Usage: {} <file1> <file2> ... <fileN>", argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
done = argc - 1;
|
|
||||||
|
|
||||||
// lio_listio
|
|
||||||
auto aiocb_list = (struct aiocb *)malloc(sizeof(struct aiocb) * (argc - 1));
|
|
||||||
auto aiocb_list_ptr = (struct aiocb **)malloc(sizeof(struct aiocb *) * (argc - 1));
|
|
||||||
|
|
||||||
// char *memchnk = (char *)malloc(5 * 1024 * 1024 * (argc - 1));
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < argc - 1; i++) {
|
|
||||||
aiocb_list[i].aio_fildes = open(argv[i + 1], O_RDONLY);
|
|
||||||
aiocb_list[i].aio_offset = 0;
|
|
||||||
// 5mb
|
|
||||||
aiocb_list[i].aio_buf = malloc(5 * 1024 * 1024);
|
|
||||||
aiocb_list[i].aio_nbytes = (5 * 1024 * 1024);;
|
|
||||||
|
|
||||||
aiocb_list[i].aio_sigevent.sigev_notify = SIGEV_THREAD;
|
|
||||||
aiocb_list[i].aio_sigevent.sigev_notify_function = aio_completion_handler;
|
|
||||||
aiocb_list[i].aio_sigevent.sigev_notify_attributes = nullptr;
|
|
||||||
// aiocb_list[i].aio_sigevent.sigev_value.sival_ptr = &aiocb_list[i];
|
|
||||||
aiocb_list[i].aio_sigevent.sigev_value.sival_ptr = new info{
|
|
||||||
argv[i + 1],
|
|
||||||
&aiocb_list[i]};
|
|
||||||
|
|
||||||
// aiocb_list[i].aio_reqprio = SIGRTMIN;
|
|
||||||
|
|
||||||
aiocb_list_ptr[i] = &aiocb_list[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
lio_listio(LIO_WAIT, aiocb_list_ptr, argc - 1, nullptr);
|
|
||||||
|
|
||||||
while (done > 0) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::println("Done reading files, {} done", done);
|
|
||||||
|
|
||||||
double capitalizedPercentage = (totalWordCount > 0)
|
|
||||||
? static_cast<double>(totalCapitalizedCount) / totalWordCount * 100.0
|
|
||||||
: 0;
|
|
||||||
double forbiddenPercentage = (totalWordCount > 0)
|
|
||||||
? static_cast<double>(totalForbiddenCount) / totalWordCount * 100.0
|
|
||||||
: 0;
|
|
||||||
double wordCountPerSentence = (totalSentenceCount > 0)
|
|
||||||
? static_cast<double>(totalWordCount) / totalSentenceCount
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
std::println(
|
|
||||||
"Word Count: {}\nCapitalized Count: {}\nSentence Count: {}\nNumber Count: {}\nForbidden Count: {}\nFile Count: {}\nFail Count: {}\nCapitalized Percentage: {}%\nForbidden Percentage: {}%\nWord Count Per Sentence: {}",
|
|
||||||
totalWordCount, totalCapitalizedCount, totalSentenceCount, totalNumberCount, totalForbiddenCount, fileCount, failCount,
|
|
||||||
capitalizedPercentage, forbiddenPercentage, wordCountPerSentence
|
|
||||||
);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < argc - 1; i++) {
|
|
||||||
close(aiocb_list[i].aio_fildes);
|
|
||||||
free((void *)aiocb_list[i].aio_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(aiocb_list);
|
|
||||||
free(aiocb_list_ptr);
|
|
||||||
// free(memchnk);
|
|
||||||
|
|
||||||
if (failCount > 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+181
-123
@@ -4,8 +4,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "rstr.h"
|
|
||||||
#include "rstring_list.h"
|
#include "rstring_list.h"
|
||||||
|
#include "rstr.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#define sl rstring_list_t
|
#define sl rstring_list_t
|
||||||
@@ -25,16 +25,24 @@ char *forbidden_words[] = {
|
|||||||
"transaction", "essential", "managing", "contact", "contacting", "understanding", "assets", "funds", NULL};
|
"transaction", "essential", "managing", "contact", "contacting", "understanding", "assets", "funds", NULL};
|
||||||
|
|
||||||
|
|
||||||
bool stricmp(char *word1, char *word2) {
|
bool show_capitalized = false;
|
||||||
while (*word1 && tolower(*word1) == tolower(*word2)) {
|
bool show_sentences = false;
|
||||||
word1++;
|
bool show_words = false;
|
||||||
word2++;
|
bool show_numbers = false;
|
||||||
|
bool show_forbidden_words = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool file_exists(char * path){
|
||||||
|
FILE * f = fopen(path, "r");
|
||||||
|
bool result = f != NULL;
|
||||||
|
if(f){
|
||||||
|
fclose(f);
|
||||||
}
|
}
|
||||||
return *word1 == *word2;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void sld(sl *lst) {
|
void sld(sl *lst) {
|
||||||
for (ulonglong i = 0; i < lst->count; i++) {
|
for (ulonglong i = 0; i < lst->count; i++) {
|
||||||
printf("<%llu:%s>\n", i, lst->strings[i]);
|
printf("<%llu:%s>\n", i, lst->strings[i]);
|
||||||
@@ -57,7 +65,6 @@ char *remove_preserved_chars(char *content) {
|
|||||||
}
|
}
|
||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
//Memory usage: 29 TB, 213.322.618 (re)allocated, 106.670.251 unqiue free'd, 0 in use.
|
|
||||||
|
|
||||||
char *slds(sl *lst) {
|
char *slds(sl *lst) {
|
||||||
str_t *buffer = strn(1337);
|
str_t *buffer = strn(1337);
|
||||||
@@ -74,6 +81,20 @@ char *slds(sl *lst) {
|
|||||||
|
|
||||||
bool isws(char c) { return c == '\t' || c == '\n' || c == ' ' || c == ','; }
|
bool isws(char c) { return c == '\t' || c == '\n' || c == ' ' || c == ','; }
|
||||||
|
|
||||||
|
char *stripws(char *content) {
|
||||||
|
char *cc = (char *)malloc(strlen(content) + 1);
|
||||||
|
*cc = 0;
|
||||||
|
char *ccp = cc;
|
||||||
|
while (*content) {
|
||||||
|
if (!isws(*content)) {
|
||||||
|
*ccp = *content;
|
||||||
|
ccp++;
|
||||||
|
*ccp = 0;
|
||||||
|
}
|
||||||
|
content++;
|
||||||
|
}
|
||||||
|
return cc;
|
||||||
|
}
|
||||||
|
|
||||||
char *fread_till_eof(FILE *f) {
|
char *fread_till_eof(FILE *f) {
|
||||||
char c;
|
char c;
|
||||||
@@ -85,10 +106,12 @@ char *fread_till_eof(FILE *f) {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_sentences(char *content) {
|
rstring_list_t *get_sentences(char *content) {
|
||||||
int count = 0;
|
|
||||||
|
rstring_list_t *sentences = rstring_list_new();
|
||||||
char *sentence_buffer = (char *)malloc(strlen(content) + 1);
|
char *sentence_buffer = (char *)malloc(strlen(content) + 1);
|
||||||
char *sentence_buffer_p = sentence_buffer;
|
char *sentence_buffer_p = sentence_buffer;
|
||||||
|
// rbuffer_t * buffer = rbuffer_new(NULL,0);
|
||||||
bool in_line = false;
|
bool in_line = false;
|
||||||
while (*content) {
|
while (*content) {
|
||||||
if ((*content == ' ' || *content == '\t' || *content == '\n') && !in_line) {
|
if ((*content == ' ' || *content == '\t' || *content == '\n') && !in_line) {
|
||||||
@@ -101,7 +124,7 @@ int get_sentences(char *content) {
|
|||||||
*sentence_buffer_p = *content;
|
*sentence_buffer_p = *content;
|
||||||
sentence_buffer_p++;
|
sentence_buffer_p++;
|
||||||
*sentence_buffer_p = 0;
|
*sentence_buffer_p = 0;
|
||||||
count++;
|
rstring_list_add(sentences, sentence_buffer);
|
||||||
sentence_buffer_p = sentence_buffer;
|
sentence_buffer_p = sentence_buffer;
|
||||||
*sentence_buffer = 0;
|
*sentence_buffer = 0;
|
||||||
content++;
|
content++;
|
||||||
@@ -114,55 +137,32 @@ int get_sentences(char *content) {
|
|||||||
content++;
|
content++;
|
||||||
}
|
}
|
||||||
free(sentence_buffer);
|
free(sentence_buffer);
|
||||||
return count;
|
return sentences;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rstring_list_t *get_words(char *content) {
|
||||||
bool is_forbidden_word(char *word) {
|
rstring_list_t *words = rstring_list_new();
|
||||||
|
|
||||||
for (int j = 0; forbidden_words[j] != NULL; j++) {
|
|
||||||
if (stricmp(word, forbidden_words[j])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_words(char *content, int * count_caps, int *fw_count) {
|
|
||||||
int count = 0;
|
|
||||||
char *word_buffer = (char *)malloc(strlen(content) + 1);
|
char *word_buffer = (char *)malloc(strlen(content) + 1);
|
||||||
char *word_buffer_p = word_buffer;
|
char *word_buffer_p = word_buffer;
|
||||||
*word_buffer_p = 0;
|
*word_buffer_p = 0;
|
||||||
bool has_lcase = false;
|
|
||||||
// rbuffer_t * buffer = rbuffer_new(NULL,0);
|
// rbuffer_t * buffer = rbuffer_new(NULL,0);
|
||||||
while (*content) {
|
while (*content) {
|
||||||
if (*content == ' ' || *content == '\t' || *content == '\n') {
|
if (*content == ' ' || *content == '\t' || *content == '\n') {
|
||||||
if (word_buffer_p != word_buffer) {
|
if (word_buffer_p != word_buffer) {
|
||||||
if(!has_lcase)
|
rstring_list_add(words, word_buffer);
|
||||||
{
|
|
||||||
(*count_caps)++;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
if(is_forbidden_word(word_buffer)){
|
|
||||||
(*fw_count)++;
|
|
||||||
}
|
|
||||||
word_buffer_p = word_buffer;
|
word_buffer_p = word_buffer;
|
||||||
*word_buffer = 0;
|
*word_buffer = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
has_lcase = false;
|
|
||||||
content++;
|
content++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
*word_buffer_p = *content;
|
*word_buffer_p = *content;
|
||||||
if(islower(*content) == *content)
|
|
||||||
has_lcase = true;
|
|
||||||
word_buffer_p++;
|
word_buffer_p++;
|
||||||
*word_buffer_p = 0;
|
*word_buffer_p = 0;
|
||||||
content++;
|
content++;
|
||||||
}
|
}
|
||||||
free(word_buffer);
|
free(word_buffer);
|
||||||
return count;
|
return words;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_fully_capitalized_word(char *word) {
|
bool is_fully_capitalized_word(char *word) {
|
||||||
@@ -174,24 +174,31 @@ bool is_fully_capitalized_word(char *word) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_capitalized_words(sl *all_words) {
|
sl *get_capitalized_words(char *content) {
|
||||||
int count = 0;
|
sl *capitalized_words = sln();
|
||||||
for (uint i = 0; i < all_words->count; i++) {
|
sl *sentences = get_sentences(content);
|
||||||
if (is_fully_capitalized_word(all_words->strings[i])) {
|
for (uint j = 0; j < sentences->count; j++) {
|
||||||
count++;
|
char *sentence = sentences->strings[j];
|
||||||
}
|
sl *all_words = get_words(sentence);
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
// Always skip the first word since sentences start with
|
||||||
|
for (uint i = 0; i < all_words->count; i++) {
|
||||||
|
if (is_fully_capitalized_word(all_words->strings[i])) {
|
||||||
|
rstring_list_add(capitalized_words, all_words->strings[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slf(all_words);
|
||||||
|
}
|
||||||
|
slf(sentences);
|
||||||
|
return capitalized_words;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *clean_content(char *content) {
|
char *clean_content(char *content) {
|
||||||
char *allowed_ichars = "01234567891abcdefghijklmnopqrstuvwxyz.,!?";
|
char *allowed_ichars = "01234567891abcdefghijklmnopqrstuvwxyz \n.,!?";
|
||||||
char *clean_content = (char *)malloc(strlen(content) + 1);
|
char *clean_content = (char *)malloc(strlen(content) + 1);
|
||||||
char *clean_content_p = clean_content;
|
char *clean_content_p = clean_content;
|
||||||
*clean_content_p = 0;
|
*clean_content_p = 0;
|
||||||
while (*content) {
|
while (*content) {
|
||||||
|
|
||||||
if (strchr(allowed_ichars, tolower(*content))) {
|
if (strchr(allowed_ichars, tolower(*content))) {
|
||||||
*clean_content_p = *content;
|
*clean_content_p = *content;
|
||||||
clean_content_p++;
|
clean_content_p++;
|
||||||
@@ -202,131 +209,182 @@ char *clean_content(char *content) {
|
|||||||
return clean_content;
|
return clean_content;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_numbers(char *cc) {
|
sl *get_numbers(char *content) {
|
||||||
int count = 0;
|
char *cc = clean_content(content);
|
||||||
char *ccc = cc;
|
char *ccc = stripws(cc);
|
||||||
char *cccp = ccc;
|
char *cccp = ccc;
|
||||||
|
free(cc);
|
||||||
char *number_buffer = (char *)malloc(strlen(ccc) + 1);
|
char *number_buffer = (char *)malloc(strlen(ccc) + 1);
|
||||||
*number_buffer = 0;
|
*number_buffer = 0;
|
||||||
char *number_buffer_p = number_buffer;
|
char *number_buffer_p = number_buffer;
|
||||||
|
sl *numbers = sln();
|
||||||
while (*cccp) {
|
while (*cccp) {
|
||||||
if (isdigit((*cccp))) {
|
if (isdigit((*cccp))) {
|
||||||
*number_buffer_p = *cccp;
|
*number_buffer_p = *cccp;
|
||||||
number_buffer_p++;
|
number_buffer_p++;
|
||||||
*number_buffer_p = 0;
|
*number_buffer_p = 0;
|
||||||
} else if (number_buffer != number_buffer_p) {
|
} else if (number_buffer != number_buffer_p) {
|
||||||
count++;
|
sla(numbers, number_buffer);
|
||||||
*number_buffer = 0;
|
*number_buffer = 0;
|
||||||
number_buffer_p = number_buffer;
|
number_buffer_p = number_buffer;
|
||||||
}
|
}
|
||||||
cccp++;
|
cccp++;
|
||||||
}
|
}
|
||||||
free(number_buffer);
|
free(number_buffer);
|
||||||
return count;
|
free(ccc);
|
||||||
|
return numbers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool stricmp(char *word1, char *word2) {
|
||||||
|
while (*word1 && tolower(*word1) == tolower(*word2)) {
|
||||||
|
word1++;
|
||||||
|
word2++;
|
||||||
|
}
|
||||||
|
return *word1 == *word2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containswordi(sl *words, char *word) {
|
||||||
|
for (uint i = 0; i < words->count; i++) {
|
||||||
|
if (stricmp(words->strings[i], word))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl *get_forbidden_words(char *content) {
|
||||||
|
sl *words = get_words(content);
|
||||||
|
sl *found = sln();
|
||||||
|
for (int j = 0; forbidden_words[j] != NULL; j++) {
|
||||||
|
if (containswordi(words, forbidden_words[j])) {
|
||||||
|
rstring_list_add(found, forbidden_words[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slf(words);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
unsigned int total = 0;
|
unsigned int total = 0;
|
||||||
|
|
||||||
char *readall(FILE *f) {
|
|
||||||
if (fseek(f, 0, SEEK_END) != 0) {
|
|
||||||
fclose(f);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
size_t file_size = ftell(f);
|
|
||||||
if (file_size == (size_t)-1L) {
|
|
||||||
fclose(f);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (fseek(f, 0, SEEK_SET) != 0) {
|
|
||||||
fclose(f);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
char *buffer = (char *)malloc(file_size + 1);
|
|
||||||
if (!buffer) {
|
|
||||||
fclose(f);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
size_t bytes_read = fread(buffer, 1, file_size, f);
|
|
||||||
buffer[bytes_read] = 0;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void analyze(FILE *f) {
|
void analyze(FILE *f) {
|
||||||
if(!f){
|
|
||||||
// File doesn't exist
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
total = total + 1;
|
total = total + 1;
|
||||||
|
|
||||||
printf("#%u\n", total);
|
printf("#%u\n", total);
|
||||||
|
char *data = fread_till_eof(f);
|
||||||
|
|
||||||
|
str_t *all = strn(1337);
|
||||||
|
char *sbuf = NULL;
|
||||||
|
|
||||||
char *data = readall(f);
|
|
||||||
if(!data)
|
|
||||||
return;
|
|
||||||
char *clean_data = clean_content(data);
|
char *clean_data = clean_content(data);
|
||||||
int capitalized_words = 0;
|
|
||||||
int fw = 0;
|
|
||||||
int words = get_words(data,&capitalized_words,&fw);
|
|
||||||
int sentences = get_sentences(data);
|
|
||||||
int numbers = get_numbers(clean_data);
|
|
||||||
|
|
||||||
// All words
|
free(clean_data);
|
||||||
printf("Words: %d\n", words);
|
|
||||||
|
|
||||||
// All capitalized words
|
// All capitalized words
|
||||||
printf("Capitalized words: %d\n", capitalized_words);
|
sl *capitalized_words = get_capitalized_words(data);
|
||||||
|
ulonglong capitalized_words_count = capitalized_words->count;
|
||||||
|
printf("Capitalized words: %llu\n", capitalized_words_count);
|
||||||
|
if(show_capitalized)
|
||||||
|
sld(capitalized_words);
|
||||||
|
sbuf = slds(capitalized_words);
|
||||||
|
stra(all, sbuf);
|
||||||
|
free(sbuf);
|
||||||
|
|
||||||
|
sl *sentences = get_sentences(data);
|
||||||
|
|
||||||
// All sentences
|
// All sentences
|
||||||
printf("Sentences: %i\n", sentences);
|
printf("Sentences: %llu\n", sentences->count);
|
||||||
|
if(show_sentences)
|
||||||
|
sld(sentences);
|
||||||
|
sbuf = slds(sentences);
|
||||||
|
stra(all, sbuf);
|
||||||
|
free(sbuf);
|
||||||
|
|
||||||
|
sl *words = get_words(data);
|
||||||
|
|
||||||
|
// All words
|
||||||
|
printf("Words: %llu\n", words->count);
|
||||||
|
if(show_words)
|
||||||
|
sld(words);
|
||||||
|
sbuf = slds(words);
|
||||||
|
stra(all, sbuf);
|
||||||
|
free(sbuf);
|
||||||
|
|
||||||
// Numbers
|
// Numbers
|
||||||
printf("Numbers: %d\n", numbers);
|
sl *numbers = get_numbers(data);
|
||||||
|
printf("Numbers: %llu\n", numbers->count);
|
||||||
|
if(show_numbers)
|
||||||
|
sld(numbers);
|
||||||
|
sbuf = slds(numbers);
|
||||||
|
stra(all, sbuf);
|
||||||
|
free(sbuf);
|
||||||
|
|
||||||
// Forbidden words
|
// Forbidden words
|
||||||
printf("Forbidden words: %d\n", fw);
|
sl *fw = get_forbidden_words(data);
|
||||||
|
printf("Forbidden words: %llu\n", fw->count);
|
||||||
if (words) {
|
if(show_forbidden_words)
|
||||||
double capitalized_word_percentage = 100 * ((double)capitalized_words / (double)words);
|
sld(fw);
|
||||||
|
sbuf = slds(fw);
|
||||||
printf("Capitalized percentage: %f%%\n", capitalized_word_percentage);
|
stra(all, sbuf);
|
||||||
double forbidden_word_percentage = 100 * ((double)fw / (double)words);
|
free(sbuf);
|
||||||
printf("Forbidden percentage: %f%%\n", forbidden_word_percentage);
|
strd(all);
|
||||||
ulonglong word_count_per_sentence = words / (sentences ? sentences : 1);
|
if(words->count){
|
||||||
printf("Word count per sentence: %llu\n", word_count_per_sentence);
|
double capitalized_word_percentage = 100 * ((double)capitalized_words->count / (double)words->count);
|
||||||
|
|
||||||
|
printf("Capitalized percentage: %f%%\n",capitalized_word_percentage);
|
||||||
|
double forbidden_word_percentage = 100 * ((double)fw->count / (double)words->count);
|
||||||
|
printf("Forbidden percentage: %f%%\n",forbidden_word_percentage);
|
||||||
|
ulonglong word_count_per_sentence = words->count / (sentences->count ? sentences->count : 1);
|
||||||
|
printf("Word count per sentence: %llu\n", word_count_per_sentence);
|
||||||
}
|
}
|
||||||
free(clean_data);
|
slf(capitalized_words);
|
||||||
|
slf(sentences);
|
||||||
|
slf(words);
|
||||||
|
slf(numbers);
|
||||||
|
slf(fw);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void analyze_file(char *path) {
|
void analyze_file(char *path) {
|
||||||
FILE *f = fopen(path, "r");
|
FILE *f = fopen(path, "r");
|
||||||
if(f){
|
|
||||||
analyze(f);
|
analyze(f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}else{
|
|
||||||
printf("File doesn't exist: %s\n",path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void * analyze_file_thread(void *path){
|
|
||||||
analyze_file((char *)path);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
pthread_t *threads = (pthread_t *)malloc(argc * sizeof(pthread_t));
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
pthread_create(&threads[i-1],NULL,analyze_file_thread,(void *)argv[i]);
|
if(!strcmp(argv[1],"--hide-capitalized")){
|
||||||
|
show_capitalized=false;
|
||||||
|
}else if(!strcmp(argv[1],"--show-sentences")){
|
||||||
|
show_sentences=true;
|
||||||
|
}else if(!strcmp(argv[1],"--show-words")){
|
||||||
|
show_words=true;
|
||||||
|
}else if(!strcmp(argv[1],"--show-numbers")){
|
||||||
|
show_words=true;
|
||||||
|
}else if(!strcmp(argv[1],"--hide-forbidden-words")){
|
||||||
|
show_forbidden_words=false;
|
||||||
|
}else if(!strcmp(argv[1],"help") || !strcmp(argv[1],"--help")){
|
||||||
|
printf("%s",
|
||||||
|
"Usage: spam [file] [file] [file]\n"
|
||||||
|
"Flag defaults:\n"
|
||||||
|
" hide-capitalized = true\n"
|
||||||
|
" show-sentences = false\n"
|
||||||
|
" show-words = false\n"
|
||||||
|
" show-numbers = false\n"
|
||||||
|
" hide-forbidden-words = false\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("File: %s\n", argv[i]);
|
||||||
|
analyze_file(argv[i]);
|
||||||
|
printf("%s\n", rmalloc_stats());
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
for(int i = 1; i < argc; i++){
|
|
||||||
pthread_join(threads[i-1],NULL);
|
|
||||||
}
|
|
||||||
free(threads);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
analyze(stdin);
|
analyze(stdin);
|
||||||
printf("%s\n", rmalloc_stats());
|
printf("%s\n", rmalloc_stats());
|
||||||
exit(0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
/target
|
|
||||||
/Cargo.lock
|
|
||||||
/test_books
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "jisspam"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
fxhash = "0.2.1"
|
|
||||||
tokio = { version = "1.44.1", features = ["full"] }
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
codegen-units = 1 # less means more compile work but better optimized
|
|
||||||
lto = "fat" # thin has best performance. fat the worst
|
|
||||||
strip = true
|
|
||||||
# opt-level = "z" # slows down
|
|
||||||
panic = "abort"
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
for https://retoor.molodetz.nl/retoor/isspam
|
|
||||||
|
|
||||||
extract `../books.tar.gz`
|
|
||||||
|
|
||||||
# local machine benchmarks
|
|
||||||
|
|
||||||
single threaded: `33.63373279571533`
|
|
||||||
|
|
||||||
rayon: `4.294418811798096`
|
|
||||||
|
|
||||||
tokio: `4.717588901519775`
|
|
||||||
|
|
||||||
tokio:
|
|
||||||
|
|
||||||
muncher: `2486ms`
|
|
||||||
|
|
||||||
for_loops: `1227ms`
|
|
||||||
|
|
||||||
for_loops_forbidden_only: `987ms`
|
|
||||||
|
|
||||||
trie creation and stats accumulation take 0ms
|
|
||||||
|
|
||||||
FxHashMap faster than BTreeMap
|
|
||||||
|
|
||||||
## compile options benchmarks
|
|
||||||
`lto` thin, fat doesn't change much
|
|
||||||
|
|
||||||
`codegen-units` 0, 1 doesn't change much
|
|
||||||
|
|
||||||
`opt-level = "z"` slow things down
|
|
||||||
|
|
||||||
# ubuntu terminal running
|
|
||||||
https://snek.molodetz.nl/terminal.html ubuntu running thing instructions:
|
|
||||||
```
|
|
||||||
mkdir /project
|
|
||||||
cd /project
|
|
||||||
git clone https://retoor.molodetz.nl/retoor/isspam.git
|
|
||||||
apt install valgrind curl
|
|
||||||
export RUSTUP_HOME=/project/.rustup
|
|
||||||
export CARGO_HOME=/project/.cargo
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
||||||
. "/project/.cargo/env"
|
|
||||||
cd isspam
|
|
||||||
rustup install nightly
|
|
||||||
rustup default nightly
|
|
||||||
make
|
|
||||||
make benchmark
|
|
||||||
python3 bench.py
|
|
||||||
```
|
|
||||||
|
|
||||||
clone: `git clone https://gitlab.com/jestdotty-group/draft/jisspam.git jest_rust`
|
|
||||||
|
|
||||||
edit make: `vi makefile` and add build:
|
|
||||||
```
|
|
||||||
build_jest:
|
|
||||||
@echo "compiling jest_rust project"
|
|
||||||
cd jest_rust && cargo build --release && cp target/release/jisspam ..
|
|
||||||
```
|
|
||||||
append to all script:
|
|
||||||
```
|
|
||||||
all: build run valgrind build_risspam run_risspam build_cpp build_borded_cpp build_py build_jest
|
|
||||||
```
|
|
||||||
|
|
||||||
add to bench: `vi bench.py`
|
|
||||||
```py
|
|
||||||
time_start = time.time()
|
|
||||||
subprocess.check_output('./jisspam books/*.txt', shell=True)
|
|
||||||
print("Time Jest Rust:", time.time() - time_start)
|
|
||||||
```
|
|
||||||
|
|
||||||
run: `python3 bench.py`
|
|
||||||
output looks something like this:
|
|
||||||
```
|
|
||||||
***benchmarking***
|
|
||||||
Time C: 31.315868377685547
|
|
||||||
Time Rust: 41.232205867767334
|
|
||||||
Time CPP: 20.1683189868927
|
|
||||||
Time Borded CPP: 15.468477964401245
|
|
||||||
Time Jest Rust: 54.74523115158081
|
|
||||||
Time Retoor Python: 287.63036131858826
|
|
||||||
***end benchmark***
|
|
||||||
```
|
|
||||||
|
|
||||||
add `/jisspam` to `.gitignore` to not commit the executable accidentally
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
mod parser;
|
|
||||||
mod stats;
|
|
||||||
mod trie;
|
|
||||||
|
|
||||||
use stats::Stats;
|
|
||||||
use std::{env, fs, sync::LazyLock};
|
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use trie::Trie;
|
|
||||||
|
|
||||||
static FORBIDDEN_WORDS: LazyLock<Trie> = LazyLock::new(|| {
|
|
||||||
let mut trie = Trie::default();
|
|
||||||
for word in [
|
|
||||||
"recovery",
|
|
||||||
"techie",
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
"digital",
|
|
||||||
"hack",
|
|
||||||
"::",
|
|
||||||
"//",
|
|
||||||
"@",
|
|
||||||
"com",
|
|
||||||
"crypto",
|
|
||||||
"bitcoin",
|
|
||||||
"wallet",
|
|
||||||
"hacker",
|
|
||||||
"welcome",
|
|
||||||
"whatsapp",
|
|
||||||
"email",
|
|
||||||
"cryptocurrency",
|
|
||||||
"stolen",
|
|
||||||
"freeze",
|
|
||||||
"quick",
|
|
||||||
"crucial",
|
|
||||||
"tracing",
|
|
||||||
"scammers",
|
|
||||||
"expers",
|
|
||||||
"hire",
|
|
||||||
"century",
|
|
||||||
"transaction",
|
|
||||||
"essential",
|
|
||||||
"managing",
|
|
||||||
"contact",
|
|
||||||
"contacting",
|
|
||||||
"understanding",
|
|
||||||
"assets",
|
|
||||||
"funds",
|
|
||||||
] {
|
|
||||||
trie.insert(word);
|
|
||||||
}
|
|
||||||
trie
|
|
||||||
});
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
let files = env::args().skip(1);
|
|
||||||
let mut rx = {
|
|
||||||
let (tx, rx) = mpsc::unbounded_channel();
|
|
||||||
for file in files {
|
|
||||||
let tx = tx.clone();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let mut stats = Stats::default();
|
|
||||||
//reading files in threads doesn't change speed of any sort but oh well
|
|
||||||
if let Ok(text) = fs::read_to_string(&file) {
|
|
||||||
stats.file_count += 1;
|
|
||||||
parser::for_loops::parse(&mut stats, &text);
|
|
||||||
} else {
|
|
||||||
stats.failed_file_count += 1;
|
|
||||||
}
|
|
||||||
let _ = tx.send(stats);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
rx
|
|
||||||
};
|
|
||||||
let mut stats = Stats::default();
|
|
||||||
while let Some(file_stat) = rx.recv().await {
|
|
||||||
stats += file_stat;
|
|
||||||
}
|
|
||||||
println!("{stats}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// needs ../books.tar.gz to be extracted into ../books
|
|
||||||
#[test]
|
|
||||||
fn test() {
|
|
||||||
use std::{env, fs, process::Command, time::Instant};
|
|
||||||
println!("cwd: {}", env::current_dir().unwrap().display());
|
|
||||||
|
|
||||||
//compile
|
|
||||||
let mut compile = Command::new("cargo");
|
|
||||||
let compile_arged = compile.arg("build").arg("--release");
|
|
||||||
match compile_arged.output() {
|
|
||||||
Ok(output) => println!("compiled {}", String::from_utf8_lossy(&output.stdout)),
|
|
||||||
Err(err) => eprintln!("compile failed: {err}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
//get test files
|
|
||||||
let files = fs::read_dir("../books")
|
|
||||||
.unwrap()
|
|
||||||
.map(|f| {
|
|
||||||
f.unwrap()
|
|
||||||
.path()
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.to_string()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
println!("test files found: {}", files.len());
|
|
||||||
|
|
||||||
println!();
|
|
||||||
|
|
||||||
//benchmark run
|
|
||||||
let benchmark = Instant::now();
|
|
||||||
let mut run = Command::new("target/release/jisspam");
|
|
||||||
let run_arged = run.args(files);
|
|
||||||
match run_arged.output() {
|
|
||||||
Ok(output) => println!("{}", String::from_utf8_lossy(&output.stdout)),
|
|
||||||
Err(err) => eprintln!("run failed: {err}"),
|
|
||||||
}
|
|
||||||
println!("benchmark: {}ms", benchmark.elapsed().as_millis());
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
use crate::{FORBIDDEN_WORDS, stats::Stats};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn parse(stats: &mut Stats, text: &str) {
|
|
||||||
for sentence in text
|
|
||||||
.split('.')
|
|
||||||
.map(|s| s.trim())
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
{
|
|
||||||
stats.sentence_count += 1;
|
|
||||||
for word in sentence
|
|
||||||
.split_ascii_whitespace()
|
|
||||||
.map(|s| s.trim())
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
{
|
|
||||||
stats.word_count += 1;
|
|
||||||
//get all numbers counted
|
|
||||||
let mut all_capitalized = true;
|
|
||||||
for char in word.chars() {
|
|
||||||
if char.is_numeric() {
|
|
||||||
stats.numeric_count += 1;
|
|
||||||
all_capitalized = false;
|
|
||||||
} else if !char.is_ascii_uppercase() {
|
|
||||||
all_capitalized = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if all_capitalized {
|
|
||||||
stats.capitalized_count += 1;
|
|
||||||
}
|
|
||||||
if FORBIDDEN_WORDS.contains(&word.to_lowercase()) {
|
|
||||||
stats.forbidden_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
use crate::{FORBIDDEN_WORDS, stats::Stats};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn parse(stats: &mut Stats, text: &str) {
|
|
||||||
for word in text
|
|
||||||
.split_ascii_whitespace()
|
|
||||||
.map(|s| s.trim())
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
{
|
|
||||||
if FORBIDDEN_WORDS.contains(&word.to_lowercase()) {
|
|
||||||
stats.forbidden_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod for_loops;
|
|
||||||
pub mod for_loops_forbidden_only;
|
|
||||||
pub mod muncher;
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
use crate::{FORBIDDEN_WORDS, stats::Stats};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
/// probably buggy. for example, are new lines sentences? what if the text has no last period?
|
|
||||||
pub fn parse(stats: &mut Stats, text: &str) {
|
|
||||||
let mut capitalized = true;
|
|
||||||
let mut whitespaced = false;
|
|
||||||
let mut dotted = false;
|
|
||||||
let mut word = String::new();
|
|
||||||
for char in text.chars() {
|
|
||||||
if whitespaced {
|
|
||||||
if !char.is_whitespace() {
|
|
||||||
whitespaced = false; //end whiteness
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else if char.is_whitespace() {
|
|
||||||
whitespaced = true;
|
|
||||||
stats.word_count += 1; //end of word
|
|
||||||
if capitalized {
|
|
||||||
stats.capitalized_count += 1;
|
|
||||||
} else {
|
|
||||||
//reset capitalized word
|
|
||||||
capitalized = true;
|
|
||||||
}
|
|
||||||
let lowercase_word = word.to_lowercase();
|
|
||||||
if FORBIDDEN_WORDS.contains(&lowercase_word) {
|
|
||||||
stats.forbidden_count += 1;
|
|
||||||
}
|
|
||||||
word = String::new();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if dotted {
|
|
||||||
if char != '.' {
|
|
||||||
dotted = false; //end sentencing
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else if char == '.' {
|
|
||||||
dotted = true;
|
|
||||||
stats.sentence_count += 1;
|
|
||||||
stats.word_count += 1; //end of word
|
|
||||||
if capitalized {
|
|
||||||
stats.capitalized_count += 1;
|
|
||||||
} else {
|
|
||||||
//reset capitalized word
|
|
||||||
capitalized = true;
|
|
||||||
}
|
|
||||||
let lowercase_word = word.to_lowercase();
|
|
||||||
if FORBIDDEN_WORDS.contains(&lowercase_word) {
|
|
||||||
stats.forbidden_count += 1;
|
|
||||||
}
|
|
||||||
word = String::new();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
word += &char.to_string();
|
|
||||||
if char.is_numeric() {
|
|
||||||
stats.numeric_count += 1;
|
|
||||||
capitalized = false;
|
|
||||||
}
|
|
||||||
if !char.is_ascii_uppercase() {
|
|
||||||
capitalized = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
use std::{fmt::Display, ops::AddAssign};
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Stats {
|
|
||||||
pub file_count: u32,
|
|
||||||
pub failed_file_count: u32,
|
|
||||||
|
|
||||||
pub sentence_count: u32,
|
|
||||||
pub word_count: u32,
|
|
||||||
|
|
||||||
pub capitalized_count: u32,
|
|
||||||
pub numeric_count: u32,
|
|
||||||
pub forbidden_count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign for Stats {
|
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
|
||||||
self.file_count += rhs.file_count;
|
|
||||||
self.failed_file_count += rhs.failed_file_count;
|
|
||||||
|
|
||||||
self.sentence_count += rhs.sentence_count;
|
|
||||||
self.word_count += rhs.word_count;
|
|
||||||
|
|
||||||
self.capitalized_count += rhs.capitalized_count;
|
|
||||||
self.numeric_count += rhs.numeric_count;
|
|
||||||
self.forbidden_count += rhs.forbidden_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Stats {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
writeln!(f, "file count: {}", self.file_count)?;
|
|
||||||
writeln!(f, "failed file count: {}", self.failed_file_count)?;
|
|
||||||
|
|
||||||
writeln!(f, "sentence count: {}", self.sentence_count)?;
|
|
||||||
writeln!(f, "word count: {}", self.word_count)?;
|
|
||||||
|
|
||||||
writeln!(f, "capitalized count: {}", self.capitalized_count)?;
|
|
||||||
writeln!(f, "numeric count: {}", self.numeric_count)?;
|
|
||||||
writeln!(f, "forbidden count: {}", self.forbidden_count)?;
|
|
||||||
|
|
||||||
let word_count = self.word_count as f32;
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"words per sentence average: {:.1}",
|
|
||||||
word_count / self.sentence_count as f32
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"forbidden word percentage: {:.2}%",
|
|
||||||
(self.forbidden_count as f32 / word_count) * 100.0,
|
|
||||||
)?;
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"capitalized word percentage: {:.2}%",
|
|
||||||
(self.capitalized_count as f32 / word_count) * 100.0,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
use fxhash::FxBuildHasher;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>; //simpler, slightly faster
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
|
||||||
struct Node {
|
|
||||||
end: bool,
|
|
||||||
children: FxHashMap<char, Node>,
|
|
||||||
}
|
|
||||||
#[derive(Default, Debug, Clone)]
|
|
||||||
pub struct Trie {
|
|
||||||
root: Node,
|
|
||||||
}
|
|
||||||
impl Trie {
|
|
||||||
pub fn insert(&mut self, word: &str) {
|
|
||||||
let mut node = &mut self.root;
|
|
||||||
for char in word.chars() {
|
|
||||||
node = node.children.entry(char).or_default();
|
|
||||||
}
|
|
||||||
node.end = true;
|
|
||||||
}
|
|
||||||
pub fn contains(&self, word: &str) -> bool {
|
|
||||||
let mut current_node = &self.root;
|
|
||||||
for char in word.chars() {
|
|
||||||
match current_node.children.get(&char) {
|
|
||||||
Some(node) => current_node = node,
|
|
||||||
None => return false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current_node.end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
// retoor <retoor@molodetz.nl>
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <immintrin.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define MAX_THREADS 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint64_t wc, cc, sc, nc, fc;
|
|
||||||
} Stats;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char* path;
|
|
||||||
Stats result;
|
|
||||||
} FileTask;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FileTask* tasks;
|
|
||||||
int next_task;
|
|
||||||
int total_tasks;
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
} WorkQueue;
|
|
||||||
|
|
||||||
static uint8_t is_ws[256];
|
|
||||||
static uint8_t is_upper[256];
|
|
||||||
static uint16_t fw_len_bits[256];
|
|
||||||
static __uint128_t fw_words[256][8];
|
|
||||||
static uint8_t fw_counts[256];
|
|
||||||
|
|
||||||
static void init_tables(void) {
|
|
||||||
static const char* forbidden[] = {
|
|
||||||
"recovery", "techie", "http", "https", "digital", "hack", "::", "//", "com",
|
|
||||||
"@", "crypto", "bitcoin", "wallet", "hacker", "welcome", "whatsapp", "email",
|
|
||||||
"cryptocurrency", "stolen", "freeze", "quick", "crucial", "tracing", "scammers",
|
|
||||||
"expers", "hire", "century", "transaction", "essential", "managing", "contact",
|
|
||||||
"contacting", "understanding", "assets", "funds"
|
|
||||||
};
|
|
||||||
for (int i = 0; i < 256; i++) {
|
|
||||||
is_ws[i] = (i == ' ' || i == '\t' || i == '\n' || i == '\r' || i == '\f');
|
|
||||||
is_upper[i] = (i >= 'A' && i <= 'Z');
|
|
||||||
fw_len_bits[i] = 0;
|
|
||||||
fw_counts[i] = 0;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 35; i++) {
|
|
||||||
int len = strlen(forbidden[i]);
|
|
||||||
uint8_t first = (uint8_t)forbidden[i][0];
|
|
||||||
fw_len_bits[first] |= (1u << len);
|
|
||||||
__uint128_t val = 0;
|
|
||||||
memcpy(&val, forbidden[i], len);
|
|
||||||
fw_words[first][fw_counts[first]++] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const __uint128_t len_masks[17] = {
|
|
||||||
0, 0xFFULL, 0xFFFFULL, 0xFFFFFFULL, 0xFFFFFFFFULL,
|
|
||||||
0xFFFFFFFFFFULL, 0xFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
((__uint128_t)0xFF << 64) | 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
((__uint128_t)0xFFFF << 64) | 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
((__uint128_t)0xFFFFFF << 64) | 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
((__uint128_t)0xFFFFFFFF << 64) | 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
((__uint128_t)0xFFFFFFFFFF << 64) | 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
((__uint128_t)0xFFFFFFFFFFFF << 64) | 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
((__uint128_t)0xFFFFFFFFFFFFFF << 64) | 0xFFFFFFFFFFFFFFFFULL,
|
|
||||||
(__uint128_t)-1
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline __attribute__((always_inline, hot)) int is_forbidden(const uint8_t* word, size_t len) {
|
|
||||||
if (__builtin_expect(len > 14, 0)) return 0;
|
|
||||||
uint8_t first = word[0];
|
|
||||||
uint16_t bits = fw_len_bits[first];
|
|
||||||
if (__builtin_expect((bits & (1u << len)) == 0, 1)) return 0;
|
|
||||||
__uint128_t w = 0;
|
|
||||||
memcpy(&w, word, len);
|
|
||||||
w &= len_masks[len];
|
|
||||||
__uint128_t* fwords = fw_words[first];
|
|
||||||
int cnt = fw_counts[first];
|
|
||||||
for (int i = 0; i < cnt; i++) {
|
|
||||||
if (fwords[i] == w) return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void process_file(FileTask* task) {
|
|
||||||
int fd = open(task->path, O_RDONLY);
|
|
||||||
if (fd < 0) return;
|
|
||||||
|
|
||||||
struct stat st;
|
|
||||||
if (fstat(fd, &st) < 0 || st.st_size == 0) {
|
|
||||||
close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size = st.st_size;
|
|
||||||
uint8_t* data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
||||||
close(fd);
|
|
||||||
if (data == MAP_FAILED) return;
|
|
||||||
|
|
||||||
madvise(data, size, MADV_SEQUENTIAL | MADV_WILLNEED);
|
|
||||||
|
|
||||||
uint64_t wc = 0, cc = 0, sc = 0, nc = 0, fc = 0;
|
|
||||||
|
|
||||||
const __m256i dot_vec = _mm256_set1_epi8('.');
|
|
||||||
const __m256i zero_vec = _mm256_set1_epi8('0' - 1);
|
|
||||||
const __m256i nine_vec = _mm256_set1_epi8('9' + 1);
|
|
||||||
size_t i = 0;
|
|
||||||
size_t simd_end = size & ~127ULL;
|
|
||||||
|
|
||||||
while (i < simd_end) {
|
|
||||||
__builtin_prefetch(data + i + 512, 0, 0);
|
|
||||||
__m256i v0 = _mm256_loadu_si256((__m256i*)(data + i));
|
|
||||||
__m256i v1 = _mm256_loadu_si256((__m256i*)(data + i + 32));
|
|
||||||
__m256i v2 = _mm256_loadu_si256((__m256i*)(data + i + 64));
|
|
||||||
__m256i v3 = _mm256_loadu_si256((__m256i*)(data + i + 96));
|
|
||||||
|
|
||||||
sc += __builtin_popcount(_mm256_movemask_epi8(_mm256_cmpeq_epi8(v0, dot_vec)));
|
|
||||||
sc += __builtin_popcount(_mm256_movemask_epi8(_mm256_cmpeq_epi8(v1, dot_vec)));
|
|
||||||
sc += __builtin_popcount(_mm256_movemask_epi8(_mm256_cmpeq_epi8(v2, dot_vec)));
|
|
||||||
sc += __builtin_popcount(_mm256_movemask_epi8(_mm256_cmpeq_epi8(v3, dot_vec)));
|
|
||||||
|
|
||||||
nc += __builtin_popcount(_mm256_movemask_epi8(_mm256_and_si256(_mm256_cmpgt_epi8(v0, zero_vec), _mm256_cmpgt_epi8(nine_vec, v0))));
|
|
||||||
nc += __builtin_popcount(_mm256_movemask_epi8(_mm256_and_si256(_mm256_cmpgt_epi8(v1, zero_vec), _mm256_cmpgt_epi8(nine_vec, v1))));
|
|
||||||
nc += __builtin_popcount(_mm256_movemask_epi8(_mm256_and_si256(_mm256_cmpgt_epi8(v2, zero_vec), _mm256_cmpgt_epi8(nine_vec, v2))));
|
|
||||||
nc += __builtin_popcount(_mm256_movemask_epi8(_mm256_and_si256(_mm256_cmpgt_epi8(v3, zero_vec), _mm256_cmpgt_epi8(nine_vec, v3))));
|
|
||||||
i += 128;
|
|
||||||
}
|
|
||||||
while (i < size) {
|
|
||||||
uint8_t c = data[i];
|
|
||||||
sc += (c == '.');
|
|
||||||
nc += (c >= '0' && c <= '9');
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < size) {
|
|
||||||
while (i < size && is_ws[data[i]]) i++;
|
|
||||||
if (i >= size) break;
|
|
||||||
|
|
||||||
size_t word_start = i;
|
|
||||||
int all_upper = 1;
|
|
||||||
|
|
||||||
while (i < size && !is_ws[data[i]]) {
|
|
||||||
all_upper &= is_upper[data[i]];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
wc++;
|
|
||||||
cc += all_upper;
|
|
||||||
size_t wlen = i - word_start;
|
|
||||||
if (wlen <= 14) {
|
|
||||||
fc += is_forbidden(data + word_start, wlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task->result.wc = wc;
|
|
||||||
task->result.cc = cc;
|
|
||||||
task->result.sc = sc;
|
|
||||||
task->result.nc = nc;
|
|
||||||
task->result.fc = fc;
|
|
||||||
|
|
||||||
munmap(data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* worker(void* arg) {
|
|
||||||
WorkQueue* q = (WorkQueue*)arg;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
pthread_mutex_lock(&q->mutex);
|
|
||||||
int task_id = q->next_task++;
|
|
||||||
pthread_mutex_unlock(&q->mutex);
|
|
||||||
|
|
||||||
if (task_id >= q->total_tasks) break;
|
|
||||||
process_file(&q->tasks[task_id]);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr, "Usage: %s <file1> [file2] ...\n", argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
init_tables();
|
|
||||||
|
|
||||||
int num_files = argc - 1;
|
|
||||||
FileTask* tasks = calloc(num_files, sizeof(FileTask));
|
|
||||||
if (!tasks) return 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < num_files; i++) {
|
|
||||||
tasks[i].path = argv[i + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkQueue queue = {
|
|
||||||
.tasks = tasks,
|
|
||||||
.next_task = 0,
|
|
||||||
.total_tasks = num_files,
|
|
||||||
.mutex = PTHREAD_MUTEX_INITIALIZER
|
|
||||||
};
|
|
||||||
|
|
||||||
int nthreads = num_files < MAX_THREADS ? num_files : MAX_THREADS;
|
|
||||||
pthread_t threads[MAX_THREADS];
|
|
||||||
|
|
||||||
for (int i = 0; i < nthreads; i++) {
|
|
||||||
pthread_create(&threads[i], NULL, worker, &queue);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < nthreads; i++) {
|
|
||||||
pthread_join(threads[i], NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long twc = 0, tcc = 0, tsc = 0, tnc = 0, tfc = 0;
|
|
||||||
for (int i = 0; i < num_files; i++) {
|
|
||||||
twc += tasks[i].result.wc;
|
|
||||||
tcc += tasks[i].result.cc;
|
|
||||||
tsc += tasks[i].result.sc;
|
|
||||||
tnc += tasks[i].result.nc;
|
|
||||||
tfc += tasks[i].result.fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
double cc_pct = twc > 0 ? (double)tcc / twc * 100.0 : 0;
|
|
||||||
double fc_pct = twc > 0 ? (double)tfc / twc * 100.0 : 0;
|
|
||||||
double wps = tsc > 0 ? (double)twc / tsc : 0;
|
|
||||||
|
|
||||||
printf("\nTotal Words: %llu\n", twc);
|
|
||||||
printf("Total Capitalized words: %llu\n", tcc);
|
|
||||||
printf("Total Sentences: %llu\n", tsc);
|
|
||||||
printf("Total Numbers: %llu\n", tnc);
|
|
||||||
printf("Total Forbidden words: %llu\n", tfc);
|
|
||||||
printf("Capitalized percentage: %.6f%%\n", cc_pct);
|
|
||||||
printf("Forbidden percentage: %.6f%%\n", fc_pct);
|
|
||||||
printf("Word count per sentence: %.6f\n", wps);
|
|
||||||
printf("Total files read: %d\n", num_files);
|
|
||||||
|
|
||||||
free(tasks);
|
|
||||||
pthread_mutex_destroy(&queue.mutex);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
// Author: retoor@molodetz.nl
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <thread>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#define FORBIDDEN_WORDS_COUNT 40
|
|
||||||
|
|
||||||
const std::unordered_set<std::string> forbidden_words = {
|
|
||||||
"recovery", "techie", "http", "https", "digital", "hack", "::", "//", "com",
|
|
||||||
"@", "crypto", "bitcoin", "wallet", "hacker", "welcome", "whatsapp", "email", "cryptocurrency",
|
|
||||||
"stolen", "freeze", "quick", "crucial", "tracing", "scammers", "expers", "hire", "century",
|
|
||||||
"transaction", "essential", "managing", "contact", "contacting", "understanding", "assets", "funds",
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnalysisResult {
|
|
||||||
std::string filename;
|
|
||||||
long long total_word_count = 0;
|
|
||||||
long long total_capitalized_count = 0;
|
|
||||||
long long total_sentence_count = 0;
|
|
||||||
long long total_number_count = 0;
|
|
||||||
long long total_forbidden_count = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string read_file(const std::string& filename) {
|
|
||||||
std::ifstream file(filename);
|
|
||||||
if (!file) {
|
|
||||||
std::cerr << "File doesn't exist: " << filename << std::endl;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream content;
|
|
||||||
content << file.rdbuf(); // Read the entire file into a string
|
|
||||||
return content.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void analyze_file(AnalysisResult& result) {
|
|
||||||
std::string text = read_file(result.filename);
|
|
||||||
if (!text.empty()) {
|
|
||||||
long long word_count = 0;
|
|
||||||
long long capitalized_count = 0;
|
|
||||||
long long sentence_count = 0;
|
|
||||||
long long number_count = 0;
|
|
||||||
long long forbidden_count = 0;
|
|
||||||
|
|
||||||
for (char c : text) {
|
|
||||||
if (c == '.') {
|
|
||||||
sentence_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::istringstream stream(text);
|
|
||||||
std::string token;
|
|
||||||
while (stream >> token) {
|
|
||||||
word_count++;
|
|
||||||
|
|
||||||
if (std::isupper(token[0])) {
|
|
||||||
capitalized_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::any_of(token.begin(), token.end(), ::isdigit)) {
|
|
||||||
number_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forbidden_words.find(token) != forbidden_words.end()) {
|
|
||||||
forbidden_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.total_word_count = word_count;
|
|
||||||
result.total_capitalized_count = capitalized_count;
|
|
||||||
result.total_sentence_count = sentence_count;
|
|
||||||
result.total_number_count = number_count;
|
|
||||||
result.total_forbidden_count = forbidden_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
std::cerr << "Usage: " << argv[0] << " <file1> <file2> ... <fileN>" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
std::vector<AnalysisResult> results(argc - 1);
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
results[i - 1].filename = argv[i];
|
|
||||||
threads.emplace_back(analyze_file, std::ref(results[i - 1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& thread : threads) {
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
long long total_word_count = 0;
|
|
||||||
long long total_capitalized_count = 0;
|
|
||||||
long long total_sentence_count = 0;
|
|
||||||
long long total_number_count = 0;
|
|
||||||
long long total_forbidden_count = 0;
|
|
||||||
|
|
||||||
for (const auto& result : results) {
|
|
||||||
total_word_count += result.total_word_count;
|
|
||||||
total_capitalized_count += result.total_capitalized_count;
|
|
||||||
total_sentence_count += result.total_sentence_count;
|
|
||||||
total_number_count += result.total_number_count;
|
|
||||||
total_forbidden_count += result.total_forbidden_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
double capitalized_percentage = (total_word_count > 0) ? (static_cast<double>(total_capitalized_count) / total_word_count * 100.0) : 0;
|
|
||||||
double forbidden_percentage = (total_word_count > 0) ? (static_cast<double>(total_forbidden_count) / total_word_count * 100.0) : 0;
|
|
||||||
double word_count_per_sentence = (total_sentence_count > 0) ? (static_cast<double>(total_word_count) / total_sentence_count) : 0;
|
|
||||||
|
|
||||||
std::cout << "\nTotal Words: " << total_word_count << std::endl;
|
|
||||||
std::cout << "Total Capitalized words: " << total_capitalized_count << std::endl;
|
|
||||||
std::cout << "Total Sentences: " << total_sentence_count << std::endl;
|
|
||||||
std::cout << "Total Numbers: " << total_number_count << std::endl;
|
|
||||||
std::cout << "Total Forbidden words: " << total_forbidden_count << std::endl;
|
|
||||||
std::cout << "Capitalized percentage: " << capitalized_percentage << "%" << std::endl;
|
|
||||||
std::cout << "Forbidden percentage: " << forbidden_percentage << "%" << std::endl;
|
|
||||||
std::cout << "Word count per sentence: " << word_count_per_sentence << std::endl;
|
|
||||||
std::cout << "Total files read: " << (argc - 1) << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
|
|
||||||
MAX_TEXT_LENGTH = 1024
|
|
||||||
FORBIDDEN_WORDS_COUNT = 40
|
|
||||||
|
|
||||||
forbidden_words = set([
|
|
||||||
"recovery", "techie", "http", "https", "digital", "hack", "::", "//", "com",
|
|
||||||
"@", "crypto", "bitcoin", "wallet", "hacker", "welcome", "whatsapp", "email", "cryptocurrency",
|
|
||||||
"stolen", "freeze", "quick", "crucial", "tracing", "scammers", "expers", "hire", "century",
|
|
||||||
"transaction", "essential", "managing", "contact", "contacting", "understanding", "assets", "funds",
|
|
||||||
])
|
|
||||||
|
|
||||||
class AnalysisResult:
|
|
||||||
def __init__(self, filename):
|
|
||||||
self.filename = filename
|
|
||||||
self.total_word_count = 0
|
|
||||||
self.total_capitalized_count = 0
|
|
||||||
self.total_sentence_count = 0
|
|
||||||
self.total_number_count = 0
|
|
||||||
self.total_forbidden_count = 0
|
|
||||||
|
|
||||||
def is_forbidden(word):
|
|
||||||
return word in forbidden_words
|
|
||||||
|
|
||||||
def read_file(filename):
|
|
||||||
if not os.path.exists(filename):
|
|
||||||
print(f"File doesn't exist: {filename}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
with open(filename, 'r') as file:
|
|
||||||
return file.read()
|
|
||||||
|
|
||||||
def analyze_file(result):
|
|
||||||
text = read_file(result.filename)
|
|
||||||
if text:
|
|
||||||
result.total_sentence_count = text.count('.')
|
|
||||||
tokens = text.split()
|
|
||||||
|
|
||||||
result.total_word_count = len(tokens)
|
|
||||||
result.total_capitalized_count = sum(1 for token in tokens if token[0].isupper())
|
|
||||||
result.total_number_count = sum(1 for token in tokens if any(char.isdigit() for char in token))
|
|
||||||
result.total_forbidden_count = sum(1 for token in tokens if is_forbidden(token))
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print(f"Usage: {sys.argv[0]} <file1> <file2> ... <fileN>")
|
|
||||||
return
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
with ThreadPoolExecutor() as executor:
|
|
||||||
futures = []
|
|
||||||
for filename in sys.argv[1:]:
|
|
||||||
result = AnalysisResult(filename)
|
|
||||||
results.append(result)
|
|
||||||
futures.append(executor.submit(analyze_file, result))
|
|
||||||
|
|
||||||
for future in futures:
|
|
||||||
future.result()
|
|
||||||
|
|
||||||
total_word_count = sum(result.total_word_count for result in results)
|
|
||||||
total_capitalized_count = sum(result.total_capitalized_count for result in results)
|
|
||||||
total_sentence_count = sum(result.total_sentence_count for result in results)
|
|
||||||
total_number_count = sum(result.total_number_count for result in results)
|
|
||||||
total_forbidden_count = sum(result.total_forbidden_count for result in results)
|
|
||||||
|
|
||||||
capitalized_percentage = (total_word_count > 0) * (total_capitalized_count / total_word_count * 100.0)
|
|
||||||
forbidden_percentage = (total_word_count > 0) * (total_forbidden_count / total_word_count * 100.0)
|
|
||||||
word_count_per_sentence = (total_sentence_count > 0) * (total_word_count / total_sentence_count)
|
|
||||||
|
|
||||||
print(f"\nTotal Words: {total_word_count}")
|
|
||||||
print(f"Total Capitalized words: {total_capitalized_count}")
|
|
||||||
print(f"Total Sentences: {total_sentence_count}")
|
|
||||||
print(f"Total Numbers: {total_number_count}")
|
|
||||||
print(f"Total Forbidden words: {total_forbidden_count}")
|
|
||||||
print(f"Capitalized percentage: {capitalized_percentage:.6f}%")
|
|
||||||
print(f"Forbidden percentage: {forbidden_percentage:.6f}%")
|
|
||||||
print(f"Word count per sentence: {word_count_per_sentence:.6f}")
|
|
||||||
print(f"Total files read: {len(sys.argv) - 1}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -26,6 +26,8 @@ void stra(str_t *str, const char *to_append) {
|
|||||||
if (required_new_length > str->size) {
|
if (required_new_length > str->size) {
|
||||||
str->size += required_new_length + str->buffer_size;
|
str->size += required_new_length + str->buffer_size;
|
||||||
str->content = (char *)realloc(str->content, str->size + 1);
|
str->content = (char *)realloc(str->content, str->size + 1);
|
||||||
|
} else {
|
||||||
|
// printf("NO NDEED\n");
|
||||||
}
|
}
|
||||||
strcat(str->content, to_append);
|
strcat(str->content, to_append);
|
||||||
str->content[str->length] = 0;
|
str->content[str->length] = 0;
|
||||||
@@ -47,4 +49,4 @@ char *strc(str_t *str) {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// swift-tools-version: 5.9
|
|
||||||
// retoor <retoor@molodetz.nl>
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "sisspam",
|
|
||||||
targets: [
|
|
||||||
.executableTarget(
|
|
||||||
name: "sisspam",
|
|
||||||
swiftSettings: [
|
|
||||||
.unsafeFlags(["-O", "-whole-module-optimization"])
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
// retoor <retoor@molodetz.nl>
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Dispatch
|
|
||||||
|
|
||||||
struct Stats {
|
|
||||||
var words: UInt64 = 0
|
|
||||||
var capitalized: UInt64 = 0
|
|
||||||
var sentences: UInt64 = 0
|
|
||||||
var numbers: UInt64 = 0
|
|
||||||
var forbidden: UInt64 = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
let charFlags: UnsafeMutablePointer<UInt8> = {
|
|
||||||
let t = UnsafeMutablePointer<UInt8>.allocate(capacity: 256)
|
|
||||||
t.initialize(repeating: 0, count: 256)
|
|
||||||
t[0x09] = 1; t[0x0A] = 1; t[0x0B] = 1; t[0x0C] = 1; t[0x0D] = 1; t[0x20] = 1
|
|
||||||
for i in 0x30...0x39 { t[i] |= 2 }
|
|
||||||
for i in 0x41...0x5A { t[i] |= 4 }
|
|
||||||
t[0x2E] = 8
|
|
||||||
return t
|
|
||||||
}()
|
|
||||||
|
|
||||||
let forbiddenWords: [String] = [
|
|
||||||
"recovery", "techie", "http", "https", "digital", "hack", "::", "//", "com",
|
|
||||||
"@", "crypto", "bitcoin", "wallet", "hacker", "welcome", "whatsapp", "email", "cryptocurrency",
|
|
||||||
"stolen", "freeze", "quick", "crucial", "tracing", "scammers", "expers", "hire", "century",
|
|
||||||
"transaction", "essential", "managing", "contact", "contacting", "understanding", "assets", "funds"
|
|
||||||
]
|
|
||||||
|
|
||||||
struct ForbiddenLookup {
|
|
||||||
let lengthBits: UnsafeMutablePointer<UInt16>
|
|
||||||
let wordData: UnsafeMutablePointer<UInt64>
|
|
||||||
let wordCount: UnsafeMutablePointer<UInt8>
|
|
||||||
|
|
||||||
init() {
|
|
||||||
lengthBits = UnsafeMutablePointer<UInt16>.allocate(capacity: 256)
|
|
||||||
wordData = UnsafeMutablePointer<UInt64>.allocate(capacity: 256 * 16)
|
|
||||||
wordCount = UnsafeMutablePointer<UInt8>.allocate(capacity: 256)
|
|
||||||
|
|
||||||
lengthBits.initialize(repeating: 0, count: 256)
|
|
||||||
wordData.initialize(repeating: 0, count: 256 * 16)
|
|
||||||
wordCount.initialize(repeating: 0, count: 256)
|
|
||||||
|
|
||||||
for word in forbiddenWords {
|
|
||||||
let bytes = Array(word.utf8)
|
|
||||||
guard !bytes.isEmpty && bytes.count <= 14 else { continue }
|
|
||||||
|
|
||||||
let firstChar = Int(bytes[0])
|
|
||||||
let len = bytes.count
|
|
||||||
|
|
||||||
lengthBits[firstChar] |= UInt16(1 << len)
|
|
||||||
|
|
||||||
var lo: UInt64 = 0
|
|
||||||
var hi: UInt64 = 0
|
|
||||||
for (i, b) in bytes.enumerated() {
|
|
||||||
if i < 8 {
|
|
||||||
lo |= UInt64(b) << (i * 8)
|
|
||||||
} else {
|
|
||||||
hi |= UInt64(b) << ((i - 8) * 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let idx = Int(wordCount[firstChar])
|
|
||||||
if idx < 8 {
|
|
||||||
wordData[firstChar * 16 + idx * 2] = lo
|
|
||||||
wordData[firstChar * 16 + idx * 2 + 1] = hi
|
|
||||||
wordCount[firstChar] = UInt8(idx + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@inline(__always)
|
|
||||||
func check(_ ptr: UnsafePointer<UInt8>, _ len: Int) -> UInt64 {
|
|
||||||
let firstChar = Int(ptr[0])
|
|
||||||
if (lengthBits[firstChar] & UInt16(1 << len)) == 0 { return 0 }
|
|
||||||
|
|
||||||
let raw = UnsafeRawPointer(ptr)
|
|
||||||
var lo: UInt64
|
|
||||||
var hi: UInt64 = 0
|
|
||||||
|
|
||||||
if len <= 8 {
|
|
||||||
switch len {
|
|
||||||
case 1: lo = UInt64(ptr[0])
|
|
||||||
case 2: lo = UInt64(raw.loadUnaligned(as: UInt16.self))
|
|
||||||
case 3: lo = UInt64(raw.loadUnaligned(as: UInt16.self)) | (UInt64(ptr[2]) << 16)
|
|
||||||
case 4: lo = UInt64(raw.loadUnaligned(as: UInt32.self))
|
|
||||||
case 5: lo = UInt64(raw.loadUnaligned(as: UInt32.self)) | (UInt64(ptr[4]) << 32)
|
|
||||||
case 6: lo = UInt64(raw.loadUnaligned(as: UInt32.self)) | (UInt64(raw.advanced(by: 4).loadUnaligned(as: UInt16.self)) << 32)
|
|
||||||
case 7: lo = UInt64(raw.loadUnaligned(as: UInt32.self)) | (UInt64(raw.advanced(by: 4).loadUnaligned(as: UInt16.self)) << 32) | (UInt64(ptr[6]) << 48)
|
|
||||||
default: lo = raw.loadUnaligned(as: UInt64.self)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lo = raw.loadUnaligned(as: UInt64.self)
|
|
||||||
switch len {
|
|
||||||
case 9: hi = UInt64(ptr[8])
|
|
||||||
case 10: hi = UInt64(raw.advanced(by: 8).loadUnaligned(as: UInt16.self))
|
|
||||||
case 11: hi = UInt64(raw.advanced(by: 8).loadUnaligned(as: UInt16.self)) | (UInt64(ptr[10]) << 16)
|
|
||||||
case 12: hi = UInt64(raw.advanced(by: 8).loadUnaligned(as: UInt32.self))
|
|
||||||
case 13: hi = UInt64(raw.advanced(by: 8).loadUnaligned(as: UInt32.self)) | (UInt64(ptr[12]) << 32)
|
|
||||||
default: hi = UInt64(raw.advanced(by: 8).loadUnaligned(as: UInt32.self)) | (UInt64(raw.advanced(by: 12).loadUnaligned(as: UInt16.self)) << 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let base = firstChar * 16
|
|
||||||
let count = Int(wordCount[firstChar])
|
|
||||||
for j in 0..<count {
|
|
||||||
if wordData[base + j * 2] == lo && wordData[base + j * 2 + 1] == hi {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lookup = ForbiddenLookup()
|
|
||||||
|
|
||||||
@inline(__always)
|
|
||||||
func analyzeBuffer(_ ptr: UnsafePointer<UInt8>, _ length: Int) -> Stats {
|
|
||||||
var words: UInt64 = 0
|
|
||||||
var capitalized: UInt64 = 0
|
|
||||||
var sentences: UInt64 = 0
|
|
||||||
var numbers: UInt64 = 0
|
|
||||||
var forbidden: UInt64 = 0
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
let end = length
|
|
||||||
|
|
||||||
while i < end {
|
|
||||||
let f0 = charFlags[Int(ptr[i])]
|
|
||||||
|
|
||||||
sentences &+= UInt64((f0 >> 3) & 1)
|
|
||||||
|
|
||||||
if (f0 & 1) != 0 {
|
|
||||||
i += 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let wordStart = i
|
|
||||||
capitalized &+= UInt64((f0 >> 2) & 1)
|
|
||||||
var hasDigit = UInt64((f0 >> 1) & 1)
|
|
||||||
|
|
||||||
i += 1
|
|
||||||
while i < end {
|
|
||||||
let f = charFlags[Int(ptr[i])]
|
|
||||||
if (f & 1) != 0 { break }
|
|
||||||
sentences &+= UInt64((f >> 3) & 1)
|
|
||||||
hasDigit |= UInt64((f >> 1) & 1)
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let wordLen = i - wordStart
|
|
||||||
words &+= 1
|
|
||||||
numbers &+= hasDigit
|
|
||||||
|
|
||||||
if wordLen <= 14 {
|
|
||||||
forbidden &+= lookup.check(ptr + wordStart, wordLen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stats(words: words, capitalized: capitalized, sentences: sentences, numbers: numbers, forbidden: forbidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
func processFile(_ path: String) -> Stats {
|
|
||||||
let fd = open(path, O_RDONLY)
|
|
||||||
guard fd >= 0 else { return Stats() }
|
|
||||||
defer { close(fd) }
|
|
||||||
|
|
||||||
var st = stat()
|
|
||||||
guard fstat(fd, &st) == 0 else { return Stats() }
|
|
||||||
let size = Int(st.st_size)
|
|
||||||
guard size > 0 else { return Stats() }
|
|
||||||
|
|
||||||
guard let mapped = mmap(nil, size, PROT_READ, MAP_PRIVATE, fd, 0), mapped != MAP_FAILED else {
|
|
||||||
return Stats()
|
|
||||||
}
|
|
||||||
defer { munmap(mapped, size) }
|
|
||||||
|
|
||||||
let ptr = mapped.assumingMemoryBound(to: UInt8.self)
|
|
||||||
return analyzeBuffer(ptr, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
let args = CommandLine.arguments
|
|
||||||
guard args.count > 1 else {
|
|
||||||
print("Usage: \(args[0]) <file1> <file2> ... <fileN>")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let files = Array(args.dropFirst())
|
|
||||||
let fileCount = files.count
|
|
||||||
|
|
||||||
let resultsPtr = UnsafeMutablePointer<Stats>.allocate(capacity: fileCount)
|
|
||||||
resultsPtr.initialize(repeating: Stats(), count: fileCount)
|
|
||||||
defer { resultsPtr.deallocate() }
|
|
||||||
|
|
||||||
DispatchQueue.concurrentPerform(iterations: fileCount) { idx in
|
|
||||||
resultsPtr[idx] = processFile(files[idx])
|
|
||||||
}
|
|
||||||
|
|
||||||
var total = Stats()
|
|
||||||
for i in 0..<fileCount {
|
|
||||||
let r = resultsPtr[i]
|
|
||||||
total.words &+= r.words
|
|
||||||
total.capitalized &+= r.capitalized
|
|
||||||
total.sentences &+= r.sentences
|
|
||||||
total.numbers &+= r.numbers
|
|
||||||
total.forbidden &+= r.forbidden
|
|
||||||
}
|
|
||||||
|
|
||||||
let capitalizedPct = total.words > 0 ? Double(total.capitalized) / Double(total.words) * 100.0 : 0.0
|
|
||||||
let forbiddenPct = total.words > 0 ? Double(total.forbidden) / Double(total.words) * 100.0 : 0.0
|
|
||||||
let wordsPerSentence = total.sentences > 0 ? Double(total.words) / Double(total.sentences) : 0.0
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Total Words: \(total.words)")
|
|
||||||
print("Total Capitalized words: \(total.capitalized)")
|
|
||||||
print("Total Sentences: \(total.sentences)")
|
|
||||||
print("Total Numbers: \(total.numbers)")
|
|
||||||
print("Total Forbidden words: \(total.forbidden)")
|
|
||||||
print(String(format: "Capitalized percentage: %.6f%%", capitalizedPct))
|
|
||||||
print(String(format: "Forbidden percentage: %.6f%%", forbiddenPct))
|
|
||||||
print(String(format: "Word count per sentence: %.6f", wordsPerSentence))
|
|
||||||
print("Total files read: \(fileCount)")
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user