Понадобилось мне это для того чтобы понять в каком месте и как файл JPEG испорчен в процессе передачи.
VCDIFF — формат и алгоритм для дельта кодирования. Описан в RFC 3284.
Дельта-кодирование (англ. Delta encoding) — способ представления данных в виде разницы (дельты) между последовательными данными вместо самих данных.
Для примера я использую текстовые файлы в кодировке Windows-1251 для наглядности. Но с таким же успехом это могут быть и бинарные файлы.
Исходники:
"копия текст копия" ( source.txt )
"копия изменения копия" ( target.txt )
Нужно получить разницу между файлами:
" изменения " ( source.txt -> target.txt )
" текст " ( target.txt -> source.txt )
Я пользуюсь программой xdelta3 но думаю подойдёт любая которая работает с форматом vcdiff.
Как получить
Нам понадобится ещё один файл заполненный пробелами:
" " ( spaces.txt )
Он должен быть больше или равен по размеру файлу источнику ( source.txt )
Команда:
xdelta3 -e -A -n -s source.txt target.txt | xdelta3 -d -s spaces.txt
Результат:
изменения
Использованные флаги:
-e
— создание дельты
-A
— убирает лишние заголовки
-n
— убирает crc (он не даёт применить дельту с другим источником)
-s [файл]
— источник с которым сравнивается целевой файл и восстанавливается
-d
— получение целевого файла из дельты и источника
Как это работает
Если выполнить команду:
xdelta3 -e -A -n -s source.txt target.txt | xdelta3 printdelta
То после всех заголовков увидим команды VCDIFF
Offset Code Type1 Size1 @Addr1 + Type2 Size2 @Addr2
000000 025 CPY_0 9 S@0
000009 010 ADD 9
000018 025 CPY_0 9 S@14
Формат VCDIFF по своей сути очень простой. Он состоит из 3х команд.
COPY
(копировать) — копирует данные из источника или цели
ADD
(добавить) — пишет в целевой файл данные сохранённые в дельте (уникальные данные которых нет в источнике)
RUN
(повторить) — повторяет один байт из дельты заданное количество раз
Дельта хранит только уникальные данные а остальное копирует из источника. Если выполнть команду:
xdelta3 -e -A -n -s source.txt target.txt > target.vcdiff
Мы увидим в дельте только слово "изменения" которое есть только в целевом файле
D0A6D093D094200102011720131B2009
0302изменения190D0A19200E
(JSON не любит спец символы поэтому я перевёл их в HEX)
Если дельту применить на источнике (source.txt) то мы получим целевой файл (target.txt)
xdelta3 -d -s source.txt target.vcdiff
копия изменения копия
Подменив источник (source.txt) на файл заполненный пробелами (spaces.txt) мы заменили данные которые повторяются в источнике и в целевом файле на пробелы.
xdelta3 -d -s spaces.txt target.vcdiff
изменения
В файле spaces.txt можно использовать любой другой символ. Главное условие чтобы файл spaces.txt был больше либо равен по размеру файлу источнику.
Собственно JPEG файлы я сравнивал так:
xdelta3 -e -A -n -s bad_image.jpg good_image.jpg | xdelta3 -d -s spaces.txt
Результат сравнения этих файлов:
F488A2 F2AB
Много пробелов и байты которые были "побиты". Битые байты перевёл в HEX.
Тестовые jpeg файлы на которых вы можете протестировать способы сравнения:
tortoise.jpg (18 821 b) | tortoise_bad.jpg (18 829 b) |
---|---|
xdelta3 -e -A -n -s tortoise_bad.jpg tortoise.jpg | xdelta3 -d -s spaces.txt
Результат сравнения этих файлов:
F1BF F0B786 F39BAF F3BD94
Битые байты перевёл в HEX.
Комментарии (24)
Alukardd
11.08.2018 15:53Чот как-то сложно и не удобно…
Вариант 1: использовать утилиту cmp
$ cmp -l bad.png good.png | gawk '{printf "%08X %02X %02X\n", $1-1, strtonum(0$2), strtonum(0$3)}' 00003680 00 AF 00003681 00 EB 00003682 00 0B 00003683 00 9F
Вариант 2: vim
vimdiff bad.png good.png # и в каждой панели выполнить :%!xxd
ivan386 Автор
11.08.2018 17:11На старте он конечно подсвечивает строки и места в них где произошли изменения. Это похоже на классический текстовый diff.
Я пытался. У меня пару раз завис терминал. После команды
:%!xxd
vim не даёт переключиться на другую панель командой:n
. Я бросил это дело.Alukardd
11.08.2018 17:57vimdiff это для небольших файлов (несколько десятков мегабайт, ну максимум сотня-другая), потом оно начинает ощутимо тормозить.
iig
12.08.2018 09:51hexdiff? Увидеть разницу между бинарниками достаточно.
ivan386 Автор
12.08.2018 16:05Добавил в конце тестовые jpeg файлы и результат сравнения. Вы можете на них протестировать hexdiff. У меня его нет.
iig
12.08.2018 16:44apt-get install hexdiff ;)
Два 16ричных dump'а, разница подсвечивается.ivan386 Автор
12.08.2018 18:11У меня Windows. Вопрос в том как разница подсвечивается. Если он подсветил большие участки файла то это не правильно. А если только те маленькие участки в несколько байт в которых разница то нормально.
iig
13.08.2018 14:50После обработки картинок скриптами хостинга не вижу смысла их сравнивать.
ilkdrlzomd3jwpe3s_osj-vvpfw.jpeg — 17737 байт и zkhknvuzulrcqc92aocitt6sfeg.jpeg — 18829. Отличаются внутри чуть более чем полностью. Где взять tortoise.jpg и tortoise_bad.jpg — не понял, сорри.ivan386 Автор
13.08.2018 15:48Интересно что habr пережал только хороший файл а плохой не тронул. Перезалил на github и поменял ссылки в посте.
iig
13.08.2018 16:32Спс, теперь похоже на правду.
18829 tortoise_bad.jpg
18821 tortoise.jpg
Первое отличие в позиции 0x147D
Только я все равно не понял, какая задача решалась ;)
Контроль целостности файлов?
Восстановление битого файла?
Исследование программы для обработки картинок (или протокола передачи), странно искажающей данные?ivan386 Автор
13.08.2018 16:50Я исследовал способ передачи бинарных данных в json кодируя в их utf8. Оказалось что файл бился в процессе декодирования из за того что я не учитывал суррогатные символы.
Вот с помощью этого способа я и увидел проблемные байты.
iig
13.08.2018 17:06Понятно. google://json binary data говорит, что json это не умеет. И завернуть binary data в utf-8 строку это тоже надо сильно постараться.
GH0st3rs
У меня одного вопрос: "А где описание сравнения JPEG?"
Обычный текст можно и обычным diff сравнить
ivan386 Автор
Я текст выбрал для демонстрации чтоб наглядно было и не занимало много места. Но могу приложить и портянку с результатом сравнения JPEG файлов. Там собственно много пробелов и кусочки которые были побиты.
GH0st3rs
Можно просто заменить заголовок ставить)
ivan386 Автор
Добавил в конце пример результата сравнения JPEG файлов.