Блог*
Что делает @insert_reference_here, когда у него освобождается свободное время в выходные? Ну разумеется решает заковыристую задачку с лайфтаймами с работы.
Что делает @insert_reference_here, когда у него освобождается свободное время в выходные? Ну разумеется решает чужую заковыристую задачку с
macro_rules!
.Forwarded from Reactor Live
This media is not supported in your browser
VIEW IN TELEGRAM
joy/ #interestingasfuck
Извлечение электричества из катушки Теслы с помощью шприца
Извлечение электричества из катушки Теслы с помощью шприца
#rust #gamedev
У Zemeroth число звёздочек на гитхабе перевалило за тысячу, ю-ху! 🥳
https://t.me/ozkriff_games/32
У Zemeroth число звёздочек на гитхабе перевалило за тысячу, ю-ху! 🥳
https://t.me/ozkriff_games/32
Telegram
ozkriff.games
# 1000 звезд
Курс GitHub репозитория Земерота обновил очередной исторический максимум, превысив психологический рубеж в 1000 звезд. Мелочь, но клево :)
Курс GitHub репозитория Земерота обновил очередной исторический максимум, превысив психологический рубеж в 1000 звезд. Мелочь, но клево :)
#prog #rust
Хозяйке на заметку
Как известно, в
Хозяйке на заметку
Как известно, в
macro_rules!
можно использовать спецификатор повтора ?
в форме $(some complex pattern)?
. Вопрос: что делать, если опицональный захват нужно развернуть в Some($захват)
, если он есть, и в None
, если его нету? Очень просто: достаточно делегировать эту задачу очень простому макросу make_option!
:macro_rules! make_option {Разумеется, это будет работать без сбоев только в том случае, если захват не может соответствовать пустой последовательности лексем.
() => { ::core::option::Option::None };
($($anything:tt)*) => { ::core::option::Option::Some($($anything)*) };
}
Forwarded from запуск завтра
Извините, что ночью, но исследователи утверждают, что Pegasus стабильно взламывает самую последнюю версию айфона (14.6). Без всяких кликов по ссылкам, просто посылают жертве «заряженную смску» (сообщение в iMessage) и всё, атакующий владеет телефоном.
Речь идет об использовании уязвимости нулевого дня, о которой пока не знает Apple. Благодаря этому, атакующие могут даже не сохранять взлом в долгосрочной памяти, так что после перезагрузки телефона почти все следы атаки стираются — будет нужно, взломают ещё раз. Это усложняет исследование.
Владельцы андроидов могут не радоваться, там всё настолько печально, что нет возможности даже определить, был ли взлом.
Подробная техническая статья и супер понятный пересказ в твиттере. Прям мурашки по коже, будто читаю сценарий кино, а не на самом деле.
Если вы интересны спецслужбам — выключите iMessage в настройках айфона.
Речь идет об использовании уязвимости нулевого дня, о которой пока не знает Apple. Благодаря этому, атакующие могут даже не сохранять взлом в долгосрочной памяти, так что после перезагрузки телефона почти все следы атаки стираются — будет нужно, взломают ещё раз. Это усложняет исследование.
Владельцы андроидов могут не радоваться, там всё настолько печально, что нет возможности даже определить, был ли взлом.
Подробная техническая статья и супер понятный пересказ в твиттере. Прям мурашки по коже, будто читаю сценарий кино, а не на самом деле.
Если вы интересны спецслужбам — выключите iMessage в настройках айфона.
Блог*
#prog Переписал по работе одну утилиту для анализа логов. Раньше для разбора строк использовались регулярные выражения, а я заменил на наколенный лексер. В результате утилита, которая почти 23 гигабайта перемалывает за чуть больше, чем за 5 минут, стала на…
#prog #rust #parsing #моё
Хочу поделиться своей техникой написания наколеночного парсера, которую я применил для этой утилиты. Она очень простая и потому не очень мощная, но для разбора простых форматов хватает. При этом, как показала практика, она может быть быстрее регулярок. Сразу скажу, что этот подход не подойдёт, если требуются детальные сообщения об ошибках.
Начнём с определения центральной структуры для разбора:
Начнём с метода, который будет обозначать конец разбора — он пригодится нам, чтобы удостовериться, что мы не оставили никакого мусора:
Казалось бы, что можно сделать с таким набором методов? Ну, этого уже достаточно, чтобы распарсить IPv4-адрес:
Но далеко не все форматы являются столь же простыми и линейными. Добавим чуточку больше возможностей, а именно — возможность распарсить что-то опционально. В отличие от предыдущих методов, тут
Хочу поделиться своей техникой написания наколеночного парсера, которую я применил для этой утилиты. Она очень простая и потому не очень мощная, но для разбора простых форматов хватает. При этом, как показала практика, она может быть быстрее регулярок. Сразу скажу, что этот подход не подойдёт, если требуются детальные сообщения об ошибках.
Начнём с определения центральной структуры для разбора:
#[derive(Clone)]Да, всё верно, это буквально обёртка над строковым слайсом. Все последующие методы будут принимать мутабельную ссылку на
struct Lexer<'a> {
s: &'a str,
}
impl<'a> Lexer<'a> {
fn of(s: &'a str) -> Self {
Self { s }
}
}
Lexer
и будут возвращать Option<Something>
, где Some
означает, что нечто удалось распарсить, а None
будет обозначать ошибку. При этом все эти методы будут следовать одному правилу: если что-то распарсить не удалось, то Lexer
остаётся ровно в том же состоянии, что был до вызова.Начнём с метода, который будет обозначать конец разбора — он пригодится нам, чтобы удостовериться, что мы не оставили никакого мусора:
fn end(&mut self) -> Option<()> {Следующий метод не очень полезен сам по себе, но он потребуется для других методов. Он просто будет сдвигать позицию, с которой производится парсинг:
if self.s.is_empty() {
Some(())
} else {
None
}
}
fn shift(&mut self, pos: usize) {Что ж, теперь напишем самый простой метод, который действительно что-то парсит, а именно — конкретную переданную строку:
self.s = &self.s[pos..];
}
fn literal(&mut self, literal: &str) -> Option<()> {Не менее полезным будет метод, который возвращает то, что находится до переданной строки:
self.s = self.s.strip_prefix(literal)?;
Some(())
}
fn before_literal(&mut self, literal: &str) -> Option<&'a str> {Ну и первый метод, который делает по настоящему нетривиальный разбор: парсинг беззнакового числа:
let pos = self.s.find(literal)?;
let ret = &self.s[..pos];
self.shift(pos + literal.len());
Some(ret)
}
fn number<Num: std::str::FromStr>(&mut self) -> Option<Num> {Clippy тут, к сожалению, ругается на вызов
let pos = self
.s
.as_bytes()
.iter()
.position(|ch| !ch.is_ascii_digit())
.unwrap_or(self.s.len());
let ret = self.s[..pos].parse().ok()?;
self.shift(pos);
Some(ret)
}
.unwrap_or(self.s.len())
... Точнее, ругался раньше, теперь это исправили. Отлично!Казалось бы, что можно сделать с таким набором методов? Ну, этого уже достаточно, чтобы распарсить IPv4-адрес:
#[derive(PartialEq, Debug)]Обратите внимание, благодаря выводу типов нам не пришлось уточнять тип для
struct Ip4Addr([u8; 4]);
fn parse_ip(s: &str) -> Option<Ip4Addr> {
let mut p = Lexer::of(s);
let a = p.number()?;
p.literal(".")?;
let b = p.number()?;
p.literal(".")?;
let c = p.number()?;
p.literal(".")?;
let d = p.number()?;
p.end()?;
Some(Ip4Addr([a, b, c, d]))
}
fn main() {
assert_eq!(parse_ip("12.32.200.21"), Some(Ip4Addr([12, 32, 200, 21])));
}
p.number()
.Но далеко не все форматы являются столь же простыми и линейными. Добавим чуточку больше возможностей, а именно — возможность распарсить что-то опционально. В отличие от предыдущих методов, тут
None
не будет фатальной ошибкой:fn optional<T, F: FnOnce(&mut Self) -> Option<T>>(&mut self, f: F) -> Option<T> {В принципе, теперь мы можем распарсить IP4-адрес с опциональной маской подсети, но я покажу кое-что более интересное: парсинг из задачи Advent of Code 2020.
let backtrack = self.clone();
let ret = f(self);
if ret.is_none() {
*self = backtrack;
}
ret
}
GitHub
Ignore str::len() in or_fun_call lint. by Sciencentistguy · Pull Request #6950 · rust-lang/rust-clippy
changelog: Changed or_fun_call to ignore str::len, in the same way it ignores slice::len and array::len
Closes #6943
Closes #6943
Частью решения является разбор строк вида:
Теперь нам нужно извлечь число сумок, их цвет и само слово
UPD (16/10/2022): добавил метод для парсинга числа со знаком
bright white bags contain 1 shiny gold bag.Напишем функцию для разбора одной подобной строки:
vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
faded blue bags contain no other bags.
fn parse_bags_with_amount(line: &str) -> Option<(&str, Vec<(&str, usize)>)> {Повторяющимся элементом каждой строки является подстрока "bags contain". Извлечём то, что находится до этой строки — это и будет цветом сумки, про содержимое которой идёт речь:
let mut p = Lexer::of(line);
// ...
let outer_color = p.before_literal(" bags contain ")?;А вот теперь возможны варианты. Если далее идёт "no other bags.", то на этом можно разбор заканчивать и возвращать пустой список:
if p.optional(|p| {А вот теперь нужно распарсить список значений, разделённых запятыми. К сожалению, я не придумал ничего лучшего, чем сделать безусловный цикл, который на каждой итерации будет парсить очередную порцию, а в конце итерации пытаться парсить запятую и завершать цикл, если это не удалось:
p.literal("no other bags.")?;
p.end()
})
.is_some()
{
return Some((outer_color, Vec::new()));
}
let mut inner_colors = Vec::new();Тут бы пригодился цикл с постусловием, которого в Rust, увы, нету :/
loop {
// ...
if p.optional(|p| p.literal(", ")).is_none() {
break;
}
}
Теперь нам нужно извлечь число сумок, их цвет и само слово
bag
, учтя при это, что оно может быть во множественном числе:// ...Строка заканчивается точкой:
let amount = p.number()?;
let inner_color = p.before_literal(" bag")?.trim_start_matches(' ');
p.optional(|p| p.literal("s"));
inner_colors.push((inner_color, amount));
// ...
p.literal(".")?;Если выполнение дошло до этой точки, значит, парсинг успешен:
p.end()?;
Some((outer_color, inner_colors))Проверим, как оно работает:
fn main() {Работает! Как всегда, весь код в гисте. Как вы могли заметить, техника не самая изящная, но вместе с тем она достаточно простая, чтобы её при случае, когда требуется быстро написать парсер, его можно было бы написать руками вот прямо сейчас — как я, собственно, тогда и сделал.
let input = "\
bright white bags contain 1 shiny gold bag.
vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
faded blue bags contain no other bags.";
let parsed = input
.lines()
.map(parse_bags_with_amount)
.map(Option::unwrap)
.collect::<Vec<_>>();
assert_eq!(
parsed,
[
("bright white", vec![("shiny gold", 1)]),
("vibrant plum", vec![("faded blue", 5), ("dotted black", 6)]),
("faded blue", vec![]),
],
);
}
UPD (16/10/2022): добавил метод для парсинга числа со знаком
Gist
Crude parsing
Crude parsing. GitHub Gist: instantly share code, notes, and snippets.
Уведомление по почте в час ночи: "Ваш MR [который уже пару месяцев пилится и совсем недавно пришёл в состояние, пригодное для аппрува] теперь нельзя смержить из-за конфликтов".
Кто, блин, пушает код в рабочую репу в такое время?
#трудовыебудни
Кто, блин, пушает код в рабочую репу в такое время?
#трудовыебудни
Forwarded from This Software Dev Does(n’t)? Exist
Forwarded from Канал Ивана Дианова
Ливкодинг - это вроде виджеинга, только картинка создаётся кодом. Программа пишется прямо по ходу выступления и исходник виден на экране.
Вчера впервые поливкодил на местном рейве. Думал меня хватит на полтора часа, но продержался часов шесть, пока не посветлело. Беруши помогли :-)
Забавно, что никто не отличает замысел от багов. Можно писать, что попало. А все думают, что так и было задумано. Лишь бы не черный экран.
Вчера впервые поливкодил на местном рейве. Думал меня хватит на полтора часа, но продержался часов шесть, пока не посветлело. Беруши помогли :-)
Забавно, что никто не отличает замысел от багов. Можно писать, что попало. А все думают, что так и было задумано. Лишь бы не черный экран.