Что делать – Telegram
Что делать
102 subscribers
209 photos
3 videos
4 files
130 links
Не смешно
Download Telegram
Не думал, что буду форвардить посты Хауди хо. Но написано вроде как корректно и тема донесена простым языком
Вижу, что многие пользуются black, isort или как минимум форматируют код в PyCharm. Инструменты хорошие, но они ориентируются на устаревший PEP-8, поэтому в ближайшее время придётся подыскивать им замену.

Опубликован черновик нового стайл-гайда PEP-9001, который через какое-то время станет обязательным для соблюдения, поэтому рекомендую всем ознакомиться уже сейчас и присоединиться к обсуждению:

https://peps.pythondiscord.com/pep-9001/

#pep
🔥2
Как устроена цифровая подпись

К примеру - электронное письмо. Когда нужно удостовериться, что оно не было изменено в процессе передачи - используется хэш оригинального наполнения письма. И его, очевидно, можно легко изменить. Для того, чтобы это было практически невозможным - используют асимметричные алгоритмы шифрования. Например - RSA.

RSA имеет два ключа: одним мы можем только зашифровать, но не можем расшифровать, и то же самое со вторым, только наоборот. Это даёт нам возможность без зазрения совести делиться публичным ключом, ведь расшифровать зашифрованные им данные может только владелец приватного ключа. По этому принципу, к слову, работает https (да и практически вся криптография в интернете)

А непосредственно для подписи - хэш письма шифруется приватным ключом. В результате, любой, имея публичный ключ, может расшифровать хэш и сравнить с тем, что представлено в письме, однако не имея возможности этот самый хэш изменить
🤮2
Как найти источник излишнего количества аллокаций?

go build -gcflags=“-m”. Данная команда выведет результат эскейп-анализа. Другими словами, компилятор сообщит вам, какие переменные требуют аллокации на куче, а какие - спокойно себе живут на стэке. Для более подробного отчёта, можно использовать команду go build -gcflags="-m -m”
🤔1
Префиксное дерево

…также известное, как: нагруженное дерево, и trie

Это очень похоже на бинарное дерево, только имеющее иную цель, и позволяющее держать произвольное количество листьев. Суть сводится к тому, что это ассоциативная структура данных, визуализация которой видна на пикриле. Широко используется, например, в Т9

Для большего понимая, наведу такой пример: у нас есть список из вполне конкретных шаблонов, которых необходимо строго придерживаться. И, допустим, у нас стоит цель - сделать валидацию потоковой. То есть, существует корнер-кейс, при котором нам может приходить буквально по одному символу за раз. В таком случае, у нас есть всего 2 варианта решения задачи: либо буферизировать входящие данные до прекращения их передачи, либо построить префиксное дерево из шаблонов, и проверять по нему. Выглядеть это будет примерно так:


const tail = byte(0)

type Leaf struct {
char byte
leaves []Leaf
}

func (l Leaf) IsTail() bool {
return getLeaf(tail, l.leaves) != nil
}

func (l Leaf) Validate(data []byte) (isValid bool) {
if len(data) == 0 {
return l.IsTail()
}

leaf := getLeaf(data[0], l.leaves)

if leaf == nil {
return false
}

return leaf.Validate(data[1:])
}

func getLeaf(char byte, leaves []Leaf) *Leaf {
for _, leaf := range leaves {
if leaf.char == char {
return &leaf
}
}
return nil
}


Данным кодом мы реализовали рекурсивный проход по префиксному древу. Как мы можем видеть, в методе IsTail() мы возвращаем логическое значение, является ли текущий узел хвостовым посредством поиска “магической” константы в списке своих узлов. Метод Validate() же возвращает логическое значение, совпадает ли поданная строка посимвольно с деревом. В случае, если поданная строка закончилось, однако дерево подразумевает продолжение (т.е. последняя ветка не является хвостовой), то такая строка тоже отсекается как невалидная.

Забегая вперёд, хочу заметить, что также есть вариант решения задачи итеративным методом. И в таком случае, мы можем достичь ленивого прохода - например, пройтись по древу с одной половиной данных, подождать вторую, и тогда уже допройти. Однако в таком случае, пост слишком затянется

Важно упомянуть, что по скорости данное решение будет не то, чтобы медленнее, а даже местами быстрее хэшмапы и сбалансированного дерева на получение по ключу. По памяти - тоже выигрыш, однако при условии, что наше дерево будет “сжато” - то есть, промежуточные узлы, ведущие к единственному не-промежуточному узлу. Например, дерево с узлами a->b->c->d|e можно сжать до вида abc->d|e, другими словами, объеденив несколько промежуточных узлов в один
👍21
Forwarded from Subchannel
Интересно кстати. Знал что криптография на элиптических кривых быстрее чем на умножении натуральных чисел.
Но оказывается там не всё так просто. например подпись ed25519 в ~10 раз быстрее чем rsa2048 и в ~100 раз быстрее rsa4096. А вот проверка ВНЕЗАПНО у ed25519 в 2 раза медленнее, чем rsa4096 и в 7 раз медленее, чем rsa2048.
Так что если у вас есть софт, который проверяет много раз в секунду подписи, которые выдаются редко (например jwt токены), то rsa2048, возможно, будет вариантом лучше.
Век живи - век учись.
👏1🤯1
Модификация именованного возврата через defer

Встретил недавно прикольную штуку - при именованном возврате возвращаемое значение можно подменять в defer'e. Выглядит это примерно так:


func MyFunc() (result bool) {
defer func() {
result = true
}()

return
}


Логично догадаться, что defer выполняется при выходе из функции. А поскольку мы используем именной возврат, возвращается конкретно переменная result - которая по-умолчанию равняется false, однако благодаря defer'у становится true

Пользуясь случаем, передаю привет Косте
👍1
Я вспомнил пароль от канала

Ввожу в курс дела: я активно занят разработкой вебсервера. Естественно, у меня часто возникают курьёзы, и просто забавные ситуации, о которых я здесь и буду рассказывать

Начну, пожалуй, с того, как пофиксив проблему, я обнаружил баг в тестах

Суть в том, что я люблю переиспользуемость. Больше переиспользуемости богу переиспользуемости! И, исходя из этого, мапа с заголовками запроса каждый раз была одна и та же. И она никак не очищалась. То есть, заголовки предыдущего запроса вдруг оказывались с заголовками следующего

Починив проблему, тесты парсера вдруг улетели в дедлок. Потупив полчаса, я осознал, что все тесты проходят, кроме одного - странная особенность фреймворка. Посмотрев подробнее, что не так, я осознал, что я хочу видеть в запросе заголовок, как и во всех остальных тестах. А в самом запросе его нет. И работало это как раз таки потому, что другие тесты оставляли после себя этот самый заголовок!

Забавно фиксить проблему в коде, в следствии чего находить баг в тестах.
🔥2
Кстати, ещё интересных штук о заголовках.

У парсера есть свой буфер, куда он кладёт текущий заголовок (ключ и значение). Как только он кончается - его нужно куда-то копировать, иначе записанное значение будет перезаписано последующими

Решение в лоб - на каждое значение аллоцировать свой массив, который будет после аккуратно складироваться в sync.Pool. Но почему бы не взять один огромный слайс, в котором будут лежать все значения?

Ведь у нас есть слайсы, которые могут указывать только на свою часть!

Да, у этого способа есть свои проблемы. Например, при росте буфера, все его предыдущие версии будут оставаться висеть, и GC не сможет их очистить, потому что они всё ещё нам нужны. Однако случается это один раз (потом буфер будет больше, и вмещать в себе всё, что надо), и висеть мёртвым грузом старые версии будут лишь на время обработки запроса, после чего со спокойной душой смогут быть очищены GC.

Преимущество этого способа в том, что нам даже sync.Pool не нужен - вот он, один наш слайс, всегда здесь, при окончании обработки запроса просто buffer = buffer[:0], и готово!
It ain't much, but it's honest work
🥰1
Начал трогать CI в виде гитхаб-экшонов. Оказывается, не настолько и страшная штука!

При каждом пулл-реквесте в указанные ветки (у меня это мастер) прогоняет тесты, билдит примеры (проверяя их тем самым на работоспособность), и заодно прикрутил ещё CodeQL-анализ. Последнее, правда, пока что бесполезное, но выглядит круто, и в случае чего - перепроверяет, что в мастере будет работоспособная штука

Был у меня ещё, к слову, забавный момент. У меня также подключён deepsource, и при одном из PR он заблокировал мерж из-за потенциального бага. Оказывается, я передавал инстанс не по указателю, а по значению, из-за чего важная часть сервера потенциально не работала. Круто, скажете вы? А вот это должно было быть покрыто тестами, отвечу я
Forwarded from Котб в пакете (0𝔁𝓒𝓪𝓽𝓟𝓚𝓖)
Чистота функций

Всё же считаю следует рассказать об этом. Частично буду рассказывать на примере хаскелля

Сама по себе чистая функция - это функция без побочных эффектов. То есть, фактически, это отсутствие каких-либо мутаций объектов. Например, в хаскелле это возведено в абсолют, и (практически) любая функция в нём есть чистая. Даже добавление элемента в список создаёт новый изменённый список

* Практически любая - потому что мир неидеален, и любое взаимодействие с ОС является грязным; подобные функции там обозначаются специальным типом IO

Естественно, не одним лишь хаскеллем едины. Писать чистые функции можно на практически любом (исключая всеразличные экзотические языки, кто их там знает) языке, вопрос лишь в том, что с этим могут происходить нюансы.

Продолжение идеи в посте ниже 👇
Допустим, у меня есть подобный конструктор ответов (пикрил 1). В каждый метод (пикрил 2) я передаю структуру по значению - то есть, она копируется. А значит, исходную структуру я не мутирую - это чистые методы.

А вот нюанс (как и написано в комментарии) заключается в заголовках. Это хэшмапа, которая не копируется при передаче по значению, ведь внутри она указывает на конкретные участки памяти. И это может привести к неприятным последствиям в виде передачи заголовков от предыдущего запроса в следующем

Выход в данной ситуации - сделать структуру как nil и не давать пользователю к ней прикасаться. В таком случае, в методе WithHeader нам приходится проверять на nil (и если хэшмапа оказывается действительно nil, создаём новую с нашем заголовком). Получилось неприятно, однако сохраняется чистота. К слову, со слайсами данный трюк был бы немного попроще🤔
> Instructions, registers, and assembler directives are always in UPPER CASE to remind you that assembly programming is a fraught endeavor. (Exception: the g register renaming on ARM.)

Вырезка из доки по go asm. Спасибо
https://github.com/golang/go/blob/master/doc/go_mem.html#L36-L41

If you must read the rest of this document to understand the behavior of your program, you are being too clever.

Don't be clever.

Ну ладно
🤔2