928 заметок с тегом

программирование

Как открыть файл в формате HLP

Потребовалось открыть файл в древнем формате HLP. Оказавается сейчас его ничего не открывает, даже операционная система Виндоуз, под которую он когда-то и был создан.

Возможно существует какой-то быстрый и приятный путь читать такие файлы, но мне на него выйти не удалось. Гуглёжка принесла не первой свежести утилиту winhelpcgi, которая просто так не скомпилировалась, пришлось разбираться и патчить:

diff --ignore-blank-lines --minimal -urd a/winhelpcgi/dib2png.c b/winhelpcgi/dib2png.c
--- winhelpcgi-1.0/winhelpcgi/dib2png.c	2004-06-15 11:38:40.000000000 +0400
+++ winhelpcgi-1.0-patched/winhelpcgi/dib2png.c	2018-11-03 13:59:03.405031382 +0300
@@ -85,7 +85,7 @@
   /* Set error handling.  REQUIRED if you aren't supplying your own
 	* error hadnling functions in the png_create_write_struct() call.
 	*/
-  if (setjmp(png_ptr->jmpbuf))
+  if (setjmp(png_jmpbuf(png_ptr)))
 	 {
       /* If we get here, we had a problem writing the file */
       png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
@@ -93,7 +93,7 @@
 	 }

   /* Set the I/O callback function to transfer directly to memory */
-  png_set_write_fn(png_ptr, (voidp) sbout, user_write_data, NULL);
+  png_set_write_fn(png_ptr, (png_voidp) sbout, user_write_data, NULL);

   /* Get access to the windows DIB data */
   lpbmi = (LPBITMAPINFOHEADER) GlobalLock(hDIB);

Утилита с грехом пополам преобразовала файл в нечитаемый ХТМЛ, который пришлось перекодировать:

/usr/local/bin/winhelpcgi.cgi --tar HELP.HLP |
php -r 'echo mb_convert_encoding(strtr(stream_get_contents(STDIN), ["\0"=>""]),
"CP1252", "HTML-ENTITIES");' > help.html

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

3 ноября   windows   программирование

Числа от «n» до нуля без сравнений

На «Хабре» интересную задачу увидел, требуется написать на Си программу, выводящую числа от «n» до нуля, не используя (скрыто или явно) операторы сравнения.

Мой вариант:

#include <stdlib.h>
#include <stdio.h>

void print(const int n) {
	printf("%d\n", n - 1);
}

int main(int argc, char* argv[])
{
	const int N = atoi(argv[argc - 1]);

	void (*f[])(int) = {print, exit};
	for (int n = -1;;n++) {
		f[~n/~N](N - n);
	}
}
31 октября   программирование   си

Слоны

Есть такая система репликации на триггерах для СУБД «Постгрес» — Slony, не знаю кто как читает это слово по всему миру, я всегда произносил его как «Сло́ни» — вслед за нашими системными администраторами.

Оказывается читать это слово следует немного иначе.

Примерно в конце девяностых на логотипе «Постгреса» появился слон, к этому логотипу, да и видимо вёрстке сайта, приложили руку россияне. Так картинка со слоном называлась «slonik.gif» и прислал её, как достоверно известно, петербуржец Дмитрий Самерсов, рисунок сделала какая-то его знакомая.

Тот самый логотип со слоном

Ян Вик, активный участник сообщества, в то время искал название для своей системы репликации и поинтересовался что значит «slonik», а потом спросил как будет много слонов, так и родилось название «Slony» — «Слоны́».

12 октября   postgres   история   программирование

mozjpeg 3.3.1

Заворачивал в пакет для седьмой ЦентОСи относительно свежий mozjpeg. В теории всё просто, на практике просто так пакет не собрался, пришлось корректировать руками. Запишу себе сюда, чтобы не забыть что делал.

Нужно отредактировать в двух местах файл mozjpeg.spec.in (ниже готовый патч под утилиту patch):

diff -urd --ignore-blank-lines --minimal a/release/mozjpeg.spec.in b/release/mozjpeg.spec.in
--- a/release/mozjpeg.spec.in	2017-07-10 13:58:14.000000000 +0300
+++ b/release/mozjpeg.spec.in	2018-10-12 12:30:04.172828265 +0300
@@ -8,7 +8,7 @@
 %define _datadir %{__datadir}

 # Path under which docs should be installed
-%define _docdir /usr/share/doc/%{name}-%{version}
+%define _docdir /opt/mozjpeg/doc/%{name}-%{version}

 # Path under which headers should be installed
 %define _includedir %{__includedir}
@@ -35,7 +35,7 @@
 Release: @BUILD@
 License: BSD-style
 BuildRoot: %{_blddir}/%{name}-buildroot-%{version}-%{release}
-Prereq: /sbin/ldconfig
+Requires(pre,preun): /sbin/ldconfig
 %ifarch x86_64
 Provides: %{name} = %{version}-%{release}, @PACKAGE_NAME@ = %{version}-%{release}, libturbojpeg.so()(64bit)
 %else

Дальше запустить:

autoreconf -fiv
./configure
make rpm

И получится пакет.

Предложил авторам утилиты правку на «Гитхабе», посмотрим примут ли.

Дополнение: ура, патч приняли в репозиторий, теперь можно не патчить самостоятельно.

12 октября   jpeg   программирование

Мелочь в синтаксисе SELECT

В синтаксисе запроса SELECT «Постгреса» есть приятная мелочь, которая хоть и нестандартна, но греет мне душу, — согласно описанию, выбираемые поля (часть между SELECT и FROM) можно опускать.

Вот как это работает в консоли:

SELECT FROM usr;
--
(588910 строк)

Кстати, можно ещё поставить там ключевое слово ALL, будет результат будет точно такой же.

Для чего это может пригодиться? Для подзапросов с EXISTS. Мне не нравится, как выглядит всем привычный вариант:

SELECT COUNT(*)
FROM document
WHERE EXISTS (
    SELECT NULL
    FROM document_n
    WHERE id = document.id
)

В подзапросе после SELECT по стандарту надо что-то указать, всё равно что, использоваться это не будет. Мне такая избыточность не по вкусу. А вот «Постгрес» позволяет ничего там не указывать:

SELECT COUNT(*)
FROM document
WHERE EXISTS (
    SELECT
    FROM document_n
    WHERE id = document.id
)

Смотрится как будто чуть почище. Знаю, многие ребята осуждают использование особенностей реализации эскуэля в конкретной СУБД, из-за возможных проблем с портированием на другие СУБД, но для нас это вряд ли проблема — в нашем продукте используется множество куда более сложных специфичных для «Постгреса» вещей.

Если когда-либо встанет вопрос о переезде на другую СУБД, то вышеописанное — самая малая из проблем и решить её просто — заменой через регулярное выражение.

18 сентября   postgres   программирование

Примерная оценка в Постгресе

Читая документацию по «Постгресу», набрёл на интересную конструкцию — TABLESAMPLE. Она позволяет обработать запросом не всю таблицу, а только указанный процент, причём строки выбираются случайным образом.

Например, встречается такая задача — показать какую-нибудь внутреннюю рекламу, для этого надо быстро выбрать какой-нибудь случайный идентификатор из таблицы, вот так это можно сделать при помощи указанной конструкции:

SELECT id FROM (
    SELECT id FROM very_big_table TABLESAMPLE SYSTEM (.0001) LIMIT 100
) _ ORDER BY RANDOM() LIMIT 1

Тут выбирается одна десятитысячная процента таблицы (0,0001%), а потом из этой выборки берётся первая строка. На настоящей таблице с почти миллиардом строк, такой запрос у меня занял около ста миллисекунд.

SYSTEM тут — метод выборки. Их всего два, второй (BERNOULLI) даёт более случайные и аккуратные результаты, но работает как-то уж очень медленно. SYSTEM работает на уровне блоков, выдавая случайные блоки целиком, и из-за этого я дополнительно сортирую результат — чтобы перемешать строки внутри блока.

А вообще TABLESAMPLE хорошая штука, когда нужно быстро прикинуть результат. Например, у вас какой-то тяжёлый запрос, а примерное значение необходимо прямо сейчас. Ограничиваем выборку каким-то статистически значимым процентом таблицы и экстраполируем, если это требуется.

Типа, сколько в среднем примерно сотрудники некой гипотетической компании тратят на обед в зависимости от пола (оценка по одному проценту данных):

SELECT sex, AVG(spent)
FROM meal TABLESAMPLE SYSTEM (1)
WHERE mtime BETWEEN SYMMETRIC '12:00:00' AND '14:00:00'
GROUP BY 1

Или сколько примерно раз я ездил такси «Везёт» за последние семь лет (оценка по пяти процентам и экстраполяция):

SELECT COUNT(*)*20
FROM taxi_call TABLESAMPLE BERNOULLI (5)
WHERE carrier='vezet' AND cdate >= '2011-08-03'
2018   postgres   программирование

Шило на мыло

Всё-таки я большой поклонник статической типизации в языках. Жаль, что ПХП она только-только начинает проникать.

Несколько лет назад, один из программистов написал в нашем продукте интересный код, который я сильно упростил, чтобы показать суть:

<?php
$array = '';
$array[PHP_INT_MAX] = 1;

Главное тут в том, что массив почему-то проинициализирован как строка.

Несмотря на эту странность, всё работало — ПХП на втором присваивании преобразовывал строку в массив. А какое-то время назад этот код стал валиться с нехваткой памяти. Я сегодня разбирался с этой ошибкой.

Оказалось, что в ПХП 7.1 и выше (мы недавно перешли с 7.0 на 7.2), преобразование в массив тут больше не происходит. Зачем-то одно странное поведение заменили другим — теперь в этом коде создаётся гигантская строка, состоящая из пробелов и в позицию PHP_INT_MAX записывается символ «единица». Вот память и кончается. 🤦

2018   php   php7   программирование

Статический анализ в Go

С интересом попробовал в действии gocritic — статический анализатор для Гоу, статья о котором в прошлом месяце появилась на «Хабре». Он в самом начале пути, но кое-что уже умеет.

Для теста взял один из внутренних проектов нашей компании — узкоспециализированный поисковый сервис, который размещает данные в памяти. Проекту четыре года, можно было ожидать, что в нём скопилось какое-то количество мусора, на который анализатор среагирует.

Просто так gocritic не запустился, упал с какой-то невнятной ошибкой. Хорошо, что я знаю одного из авторов — с его помощью удалось разобраться. Во-первых, пришлось переложить файлы так как сейчас это принято в Гоу, во-вторых, избавиться от относительных путей в импорте.

После успешного запуска получилось примерно два экрана замечаний, большей частью относящихся к устаревшим за время существования проекта практикам, но парочка замечаний была действительно полезна. Жаль, что пока нет интеграции с каким-либо редактором кода, как у прекрасного РеШарпера — он сам предлагает правку, остаётся только её принять.

Как бы то ни было, большинство замечаний были однотипными и много времени правка не заняла — помогли регулярные выражения. Теперь, даже со включенными экспериментальными и опциональными проверками, анализатор ругается только на одно место — он ошибочно считает, что раз у функций одинаковая сигнатура, то и сами функции одинаковые, рекомендует дать им имя и вызывать по имени.

Как я уже говорил, по-настоящему полезных замечаний было два.

Во-первых, в одном из циклов происходила итерация с копированием значения, цикл вызывается нередко, поэтому это заметное исправление.

rangeExprCopy: copy of utf (256 bytes) can be avoided with &utf

Во-вторых, в трёх местах анализатор посоветовал заменить «магические значения» на константы, которые оказывается (я не знал), есть в соответствующих модулях. Это полезно для читаемости кода и для самообучения — как результат я узнал что-то новое.

stdExpr: can replace "POST" with net/http.MethodPost

По моему мнению очень полезный проект, желаю ему дальнейшего развития!

2018   googlego   программирование

Падение libmemcached

С годами «Мемкешед» всё меньше кажется хорошим выбором в связке с ПХП. Всё время какие-то проблемы. Недавно столкнулись с очередной — при выставлении определённых опций распределения ключей на несколько серверов, модуль мемкешеда в ПХП иногда крашится вместе с интерпретатором.

Баг известный и проявляется на системах с libmemcached 1.0.16, а у нас ЦентОСь, там новее нету.

Пока отказались от этой опции, но когда-нибудь проблему всё равно придётся решить. То ли делать собственную сборку, то ли искать какой-нибудь доверенный репозиторий с версией посвежее.

2018   memcached   php   программирование

Третий параметр define

ПХП неисчерпаем, как атом, — читал какую-то статью о развитии языка (ссылку потерял) и узнал, что у функции define есть третий, необязательный параметр. Столько лет программирую на ПХП и не знал!

Эта функция определяет константу, а третий параметр определяет чувствительность к регистру. Если он установлен в true, то такая константа может быть использована в коде в любом регистре.

А ещё define возвращает результат — успешна ли операция:

@var_dump(define("True", false, true)); // bool(false), будет конфликтовать со встроенной константой
var_dump(define("True", false)); // bool(true)

Кстати, вторая строка константу вроде успешно устанавливает, но на деле воспользоваться ей невозможно — булевые константы языка к регистру нечувствительны и видимо перекрывают пользовательские. Странно, что тут нет ошибки, ещё один камень в огород ПХП.

2018   php   программирование
Ранее Ctrl + ↓