Внезапный факт. Костный мозг - не умный нихуя, но мозгом называется от того, что раньше всё, что внутри костей - называли мозгом. Собственно, головной мозг-то ведь тоже в кости находится.
Не переживайте, щчас вот допишу и будет целая статья про рандом. Без костных мозгов.
Не переживайте, щчас вот допишу и будет целая статья про рандом. Без костных мозгов.
👍3
О рандоме, энтропии и статистических текстах
Изначально планировалось запостить на телеграфе, там шрифт покрасивше, чем на телетайпе. Но оказалось, картинки туда больше загружать нельзя - только ссылками оставлять. Позор. Буду делать свое. Какие на жс есть wysiwyg редакторы с драг-н-дропом картинок? А еще лучше - с TeX
https://teletype.in/@floordiv/aopzfO0yNPW
Изначально планировалось запостить на телеграфе, там шрифт покрасивше, чем на телетайпе. Но оказалось, картинки туда больше загружать нельзя - только ссылками оставлять. Позор. Буду делать свое. Какие на жс есть wysiwyg редакторы с драг-н-дропом картинок? А еще лучше - с TeX
https://teletype.in/@floordiv/aopzfO0yNPW
Teletype
О рандоме, энтропии и статистических тестах
Рандом вездесущ. Рандом необходим. На рандоме строится криптография - верная служанка информационной эры и коммуникации как таковой...
🔥4❤🔥2❤1
i18n называется так, потому что internationalization, между i и n - 18 букв
И k8s
И a11y(вот только в душе не ебу, кто это такой)
И l10n - localization
И k8s
И a11y
И l10n - localization
Что делать
Мне понадобилось полтора года, чтобы понять, что вместо OR на каждый отдельный байт, я могу просто ко всему числу 0x2020202020202020 хуйнуть. Пиздец. Меня разрывает.
Тут теперь этому говну место
Мне подарили, если что.
Мне подарили, если что.
В какой-то момент вот этот цикл и условие (пикрил 1) я вынес в отдельную функцию (пикрил 2). Подумал: круто! Абстрагирую! Меньше самоповторений!
А потом присмотрелся. Мой го стал с привкусом плюсов. Что ещё хуже - с привкусом плюсовых итераторов.
Откатил.
А потом присмотрелся. Мой го стал с привкусом плюсов. Что ещё хуже - с привкусом плюсовых итераторов.
Откатил.
Забавный эффект. Чем дальше, тем сложнее писать хороший код.
Какая-то NP задача получается, блядь.
Какая-то NP задача получается, блядь.
😁1
В HTTP/1.1 есть два вида передачи тела:- Целый
- Раздробленный*
* перевёл как мог, я ебу как их локализировать. В оригинале - chunked. Да и целым я тоже хуй его, корректно ли кликать.
С целой передачей, у нас есть Content-Length, и там конкретное количество октетов*, содержащихся в теле запроса**.
* в сетевой разработке предпочитают применять понятие "октет битов" вместо "байт", поскольку, в отличии от "байт", слово "октет" чётко говорит о том, что битов там 8. А байт - он и из 6, и из 9, и из 16 битов состоять может (CDC6600, PDP-10, PDP-11).
** В rfc2616 просят игнорировать CRLF перед методом запроса, поскольку некоторые старые и хуёвые клиенты после тела добавляли неучтённые в длине CRLF. Сия помарка осталась там и до сих пор.
С chunked всё интереснее. Он сделан для ситуаций, когда длина тела заранее неизвестна, посему мы его льём "потоком". А именно - кусками - откуда и происходит его название. Каждый кусок - это
<hex len>\r\n<data>\r\n. Шестнадцатиричная длина, перенос строки*, данные, перенос строки. Тело считается завершённым тогда, когда мы получаем чанк с нулевой длиной.* CRLF остался стандартным разделителем и до сих пор в HTTP/1.1, и пошло это ещё со времён HTTP/0.9. Unix (точнее, все unix-like уже на тот момент - линукс, бсд, OSX) издревна понимал просто LF (
\n), а вот винда, как обычно, отличилась, и хотела именно CRLF (\r\n). Поскольку упор был на гипертекстовую природу протокола, дабы быть человекочитаемым, так оно и закрепилось вплоть до самых последних rfc. Пускай некоторые имплементации и поддерживают LF-only (как, например, моя - минутка саморекламы), но такое поведение конформным не считается. В ABNF-нотации в стандарте синтаксис определяет конкретно и только CRLF.Им удобно отдавать, например, потоковый JSON ответ с сервера. Ещё очень удобно таким образом файлы лить, но их лучше совать целиком. Потому что тогда за цену 1.5 сисколлов* мы можем задействовать
sendfile(2), находясь под линуксом. А я нахожусь под линуксом, мне поебать. Zero-copy передача файла - это вот очень хорошо, на самом деле. Линусу это, кстати, не нравится.* 1 - чтобы размер файла получить, 0.5 - заголовки придётся записать отдельно от тела, но для файлов больше пары килобайт таких записей всё равно будет несколько - в результате всё равно остаёмся в выигрыше.
А ещё, благодаря такому стримингу тела, можно удобно воротить нотификации и лонгполлинг без прибегания к вебсокетам, да. Единственное - IDLE подключения всё ещё мрут по таймауту.
В более новых стандартах, у чанков ещё и появились экстеншены. Пары ключ-значение, идущие сразу после длины блока, разделённые через
;. С ними можно получить практически HTTP внутри HTTP. Жаль только мало кто их поддерживает - с точки зрения дизайна вебфреймворка/клиента ебливо. Правда, AWS S3 их использует как раз - и это проблема. Не для меня, конечно, а для тех, кто попытается распопробовать мою индигу с AWS.—
Вообще, я это всё писал, чтобы погрузить в детали перед тем, как свою проблему вывалить. Но сработал прекраснейший метод утёнка, а столь распрекраснейший текст грешен же, коли в небытие канет. Нахуй я его тогда писал, иными словами. Посему наслаждайтесь вбросом без контекста, господа.
GitHub
GitHub - indigo-web/indigo: Indigo is a blazingly fast web-framework
Indigo is a blazingly fast web-framework. Contribute to indigo-web/indigo development by creating an account on GitHub.
🔥3🥰2
Что делать
Думаю, все знают, что можно после запуска тестов получить профайл покрытия кода. Но я лично только сейчас узнал, что из коробки есть go tool cover. Он в том числе может открывать страничку, где подсвечен код, который покрыт тестами, а который - не покрыт …
Кстати, хочу напомнить об этой годноте.
Забавно, но сегодня я проверял, что забыл покрыть, в этом же файле, если не учитывать, что успел переименовать и переписать наполнение фактически с нуля ) И, к слову, две довольно глупых строчки, которые я из принципа зелёными сделать хотел - оказались бажными. Пофиксил. Радостно.
Забавно, но сегодня я проверял, что забыл покрыть, в этом же файле
🔥1
2 месяца спустя, я наконец выкатил новый релиз. Пощипал баги, заделал кодеки (теперь и сжимать, и разжимать!), кучу всяких внутренних штук переделал, чтобы было красиво. Ну и тестами немного докрыл. Работы осталась куча, 0.17.3 ещё месяц-второй ждать, но пока выглядит работоспособно и вполне себе ебабельно.
Буду рад какому-либо фидбеку. Если по внутрянке - то ещё лучше.
А вот по доке не надо. Я и сам вижу, какая она ущербная:( Лучше правда не получается. Простите.
И лого. Я и сам знаю, что это какая-то ЛЛМка скорее, нежели вебфреймворк. Мне и самому на первый взгляд чатгпт. Но вы не подумайте, на самом деле это очень близко к символу семени жизни, просто без круга в центре.Конченное лого.
https://github.com/indigo-web/indigo
Буду рад какому-либо фидбеку. Если по внутрянке - то ещё лучше.
А вот по доке не надо. Я и сам вижу, какая она ущербная:( Лучше правда не получается. Простите.
И лого. Я и сам знаю, что это какая-то ЛЛМка скорее, нежели вебфреймворк. Мне и самому на первый взгляд чатгпт. Но вы не подумайте, на самом деле это очень близко к символу семени жизни, просто без круга в центре.
https://github.com/indigo-web/indigo
GitHub
GitHub - indigo-web/indigo: Indigo is a blazingly fast web-framework
Indigo is a blazingly fast web-framework. Contribute to indigo-web/indigo development by creating an account on GitHub.
🎉6🔥1
Забавная ситуация произошла как-то, кстати. Когда я релизнул 0.17.0, решил сразу обновить pavlo.ooo, сократить параметр name до просто n (с телефона чтобы удобнее набирать было; добавить скрытый текстовый бокс мне религия не позволяет), и добавить вариант вообще без параметра это сделать - через динамический роутинг. Это оказалась проблема - внезапно я начал ловить ошибку синтаксиса темплейта. Ошибку, которой быть не должно - я даже в тестах так и не смог репродуцировать (правда, я и не сильно-то старался, в общем).
Вернулся в окно с индигой. Посмотрел я на тот парсер темплейтов. Если не ошибаюсь, это говно было написано году в 22-23, и парсило примерно следующее:
Кстати, по поводу работать. С деревьями мне всегда было тяжко, и в первые разы моего освоения подвида префиксных (о них я, кстати, писал здесь) я не придумал ничего лучшего, чем разбить путь на сегменты - сплит по слэшам - и их как атом использовать. То есть, там каждый узел имел массив из наследников - у каждого наследника свой value, который показывал его сегмент. И вот так вот проходись по слайсу и сравнивай каждую строку. Херово, но худо-бедно в концепцию ещё вписывается, ок. Кстати, не мапа потому, что... А я не знаю, почему. С мапой было бы проще. И, в отличии от заголовков (которые я тоже в слайсе храню!), там те мапы не нужно было бы очищать, что и представляет из себя главную статью расходов при их реюзе. В общем, неопытность.
Сел и переписал всё с нуля. Теперь вайлдкарды, которые раньше в {...} заключены были, поменял, и сделал
Я, конечно, не помню, поддерживалось ли раньше что-то вроде
Но, собственно, а что же в новом дереве поменялось-то?
Вернулся в окно с индигой. Посмотрел я на тот парсер темплейтов. Если не ошибаюсь, это говно было написано году в 22-23, и парсило примерно следующее:
/user/{id}/post/{postId}. На тот момент я почему-то решил сделать для этого парсер на машине состояний (через годика два я начну лениться и хуярить на strings.IndexByte), чтобы лучше валидировать, ошибочки покрасивше показывать, по красоте, в общем. Тестами вроде как даже покрыл, работать должно было норм.Кстати, по поводу работать. С деревьями мне всегда было тяжко, и в первые разы моего освоения подвида префиксных (о них я, кстати, писал здесь) я не придумал ничего лучшего, чем разбить путь на сегменты - сплит по слэшам - и их как атом использовать. То есть, там каждый узел имел массив из наследников - у каждого наследника свой value, который показывал его сегмент. И вот так вот проходись по слайсу и сравнивай каждую строку. Херово, но худо-бедно в концепцию ещё вписывается, ок. Кстати, не мапа потому, что... А я не знаю, почему. С мапой было бы проще. И, в отличии от заголовков (которые я тоже в слайсе храню!), там те мапы не нужно было бы очищать, что и представляет из себя главную статью расходов при их реюзе. В общем, неопытность.
Сел и переписал всё с нуля. Теперь вайлдкарды, которые раньше в {...} заключены были, поменял, и сделал
/user/:id/post/:postId. Это очень правильное решение, на самом деле, потому что теперь, вместо отдельного выделения вайлдкарда собственной парой открывающего и закрывающего символов, у нас остаётся только один - это двоеточие. Неявной закрывающей "кавычкой" представляется слэш, отделяющий текущий сегмент от следующего - а это важно, потому что он служит опорной точкой при матчинге. Иначе приходилось бы изъёбываться с поиском подстроки какими-то более умными алгоритмами, чем мой излюбленный strings.IndexByte. Ну, а ещё, мне теперь нужно меньше валидировать - потому что между окончанием вайлдкарда и закрывающим слэшем больше не может ничего стоять:)Я, конечно, не помню, поддерживалось ли раньше что-то вроде
/user{id}, или вайлдкард был обязан растягиваться на целый сегмент (скорее всего), но в новом дереве я и это добавил. Стало красиво и круто.Но, собственно, а что же в новом дереве поменялось-то?
Я выкинул эту хуйню с сегментами. Теперь, когда я вставляю строку - прохожусь по всем наследникам ноды (корневая нода и кличется деревом, представляя из себя входной узел - но фактически является совершенно обыкновенной нодой; можно взять любую ноду из любой части дерева, и она будет точно так же функционировать, как полноценное дерево - пускай и подрезанное). Если вижу, что ключ имеет общий префикс со значением наследника - нахожу как раз этот самый общий префикс, и если его длина равна длине значения наследника - просто обрезаю ключ и сую его дальше. В противном случае, конечно, приходится делать немного противные вещи с тем, чтобы обрезать ещё и значение наследника, и заменить его новым узлом со значением, равным как раз общему префиксу.
Но это классическое префиксное дерево (пускай и осилил его сделать я спустя пару лет). Именно динамическим его делает то, что у каждого узла также есть указатель на так званую динамическую ноду. Одну-единственную. Она там на случай, если при непосредственно матчинге строки не нашлось подходящего наследника - и тогда, если указатель не нулевой, мы просто берём и ебашим из строки всё до первого слэша. Что срезали, то кладём в хранилище, всё остальное матчим, как обычно - у динамической ноды есть указатель на обычную, с которой мы и продолжаем процесс.
Таким лёгким манёвром мы сделали так, чтобы можно было определять неконфликтующие роуты
Ещё я потом между делом добавил greedy wildcards, в стандартном mux подглядел , и заменил ими костыльные path catcher'ы - аж от души отлегло.
Но вот незадача: тут-то уж точно хэшмапы не вкорячишь. Ну, то есть, можно было бы, но это было бы крайне неудобно из-за того, что при инсерте я довольно часто "ломаю" строки напополам и подменяю ноды. Приходится держать наследников всё так же в массиве. А это проблема - вполне себе может быть кейс, когда сервис насчитывает в себе 500-700 роутов. А с деревом штука такая, что чем меньше ключи похожи, тем ширше его пидорасит. Очень неприятно линейно перебирать пару сотен строк, знаете ли. Даже учитывая, что строки сами по себе не сильно-то на сравнение приятные:)
Поленившись ещё месяца 2, наконец допёр до нормальных бенчмарков (не в слепую же проблему решать?). Ситуацию спасает всеми излюбленный бинарный поиск. Удивительно просто оказалось сделать вставку упорядоченной - один циклНагло спиздил Вырезал с мясом Заинлайнил себе slices.BinarySearch, и получил очень приятные числа практически по всем бенчам. В сумме, где-то в 2-4 раза шустрее стало - особенно прирост был, очевидно, с "широкими" деревьями - где у одного узла очень много наследников (и, соответственно, каждого перебрать надо). Со 128 штуками, вместо 128 линейных сравнений, мы теперь делаем только 7 - чистая вин-вин ситуация. На удивление, "глубокие" деревья со 128 узлами вглубь тоже прирост показали.
Кстати, в slices.BinarySearch переполнения избежали более прикольным способом, чем в джабе -
Но это классическое префиксное дерево (пускай и осилил его сделать я спустя пару лет). Именно динамическим его делает то, что у каждого узла также есть указатель на так званую динамическую ноду. Одну-единственную. Она там на случай, если при непосредственно матчинге строки не нашлось подходящего наследника - и тогда, если указатель не нулевой, мы просто берём и ебашим из строки всё до первого слэша. Что срезали, то кладём в хранилище, всё остальное матчим, как обычно - у динамической ноды есть указатель на обычную, с которой мы и продолжаем процесс.
Таким лёгким манёвром мы сделали так, чтобы можно было определять неконфликтующие роуты
/user/:id и /user/me. Правда, не совсем понимаю, почему кто-то умудряется это в фичи приписывать - штука так-то дефолтная.Ещё я потом между делом добавил greedy wildcards
Но вот незадача: тут-то уж точно хэшмапы не вкорячишь. Ну, то есть, можно было бы, но это было бы крайне неудобно из-за того, что при инсерте я довольно часто "ломаю" строки напополам и подменяю ноды. Приходится держать наследников всё так же в массиве. А это проблема - вполне себе может быть кейс, когда сервис насчитывает в себе 500-700 роутов. А с деревом штука такая, что чем меньше ключи похожи, тем ширше его пидорасит. Очень неприятно линейно перебирать пару сотен строк, знаете ли. Даже учитывая, что строки сами по себе не сильно-то на сравнение приятные:)
Поленившись ещё месяца 2, наконец допёр до нормальных бенчмарков (не в слепую же проблему решать?). Ситуацию спасает всеми излюбленный бинарный поиск. Удивительно просто оказалось сделать вставку упорядоченной - один цикл
while key < predecessor.value и slices.Insert (кстати, невероятно полезный пакет). Немного сильнее попотеть пришлось над поиском - slices.BinarySearchFunc сгладил worst-case, но на более лайтовых кейсах всё-таки оверхеда прибавил. Кстати, в slices.BinarySearch переполнения избежали более прикольным способом, чем в джабе -
int(uint(a+b) >> 1). Что значит, если переполнение и случится - старший бит один хуй падёт пред побитовым сдвигом вправо:)Ещё оказалось, что OPTIONS * (он же server-wide OPTIONS), который должен возвращать список всех поддерживаемых сервером методов, имеет свою причуду. Ему нужно вернуть не просто список всех поддерживаемых методов со всех эндпоинтов, а только их юнион! Только те методы, которые унивеврсально поддерживаются каждым эндпоинтом. При том OPTIONS и TRACE идут безусловно (ты так-то обязан поддерживать TRACE, есличо).
От себя ещё докинул безусловно добавлять HEAD, если в юнион входит GET. У меня такая штука, что если прилетел HEAD запрос, но эндпоинт его не поддерживает - запрос автоматически сунется в хендлер GET. Сериализатор потом один хуй сам тело ответа отсечёт.
От себя ещё докинул безусловно добавлять HEAD, если в юнион входит GET. У меня такая штука, что если прилетел HEAD запрос, но эндпоинт его не поддерживает - запрос автоматически сунется в хендлер GET. Сериализатор потом один хуй сам тело ответа отсечёт.