Автор Тема: Правильный конвертер hex2bin  (Прочитано 865 раз)

Оффлайн Speccyfighter

  • Мастер
  • ***
  • Сообщений: 10 259
Практически все конвертеры для bash представленные в интернет, конвертирующие hex в bin, это шлак.
Почему шлак? Потому что они на конвертации теряют или старшие биты или весь старший полубайт целиком.
Зачем это нужно?
Во-первых теория:
- Байт в hex представлен двумя полубайтами. И это всегда двухсимвольное значение. Без каких-либо исключений.

Когда это нужно?
В частности при проверке или пересчёте edid. В той части его спецификации, где используются полубайты. Или описываются биты.

Единственный правильный конвертер hex2bin, описан функцией bash h2b() у немцев:
https://km.kkrach.de/p_bash_conversion
Которая должна быть описана в .bashrc.
Немцы сделали очень грамотно.

На мой взгляд единственная его проблема в том, что например двухбайтный bin, он выводит полубайтами разделёнными точками как например 0000.0110.0010.0100.

Я бы эту функцию разделил бы на две. Автодополнение при вызове функции, обрабатывается Tab -ом.
Подправленный шелл-код оригинала:

h2b-array()
h2b-array(){
    [ -z "$1" -o "$1" = "-h" ] && echo "h2b: Converts HEX to binary" && return 0
    INPUT=`echo "${@^^}" | sed 's/^0X//'`  # uppercase
    LENGTH=`echo $INPUT | wc -c`
    printf "%$(($LENGTH * 4 - 4))s\n" "$(echo "ibase=16; obase=2; $INPUT" | bc)" | sed 's/ /0/g'
}
$ h2b-array 010a010a
00000001000010100000000100001010
$ h2b-array 010a010a | wc -L # Количество бит
32

и h2b-human()
h2b-human(){
[ -z "$1" -o "$1" = "-h" ] && echo "h2b: Converts HEX to binary" && return 0
INPUT=`echo "${@^^}" | sed 's/^0X//'`  # uppercase
LENGTH=`echo $INPUT | wc -c`
printf "%$(($LENGTH * 4 - 4))s\n" "$(echo "ibase=16; obase=2; $INPUT" | bc)" | sed 's/ /0/g' | sed 's/\([0-9][0-9][0-9][0-9]\)/\1 /g' | sed 's/\.$//'
}
$ h2b-human 010a010a
0000 0001 0000 1010 0000 0001 0000 1010

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

Но в общем и целом, немцы сделали очень грамотно. Пять баллов. Весьма грамотно. Полезная вещь.
Загнал сюда. На всякий. Чтобы не потерялось. В массе программного мусора в интернет, найти это, чрезвычайно сложно.

Оффлайн andrew_b

  • Завсегдатай
  • *
  • Сообщений: 534
Re: Правильный конвертер hex2bin
« Ответ #1 : 26.08.2021 08:30:24 »
sed 's/ /0/g' | sed 's/\([0-9][0-9][0-9][0-9]\)/\1 /g' | sed 's/\.$//'
Это не выглядит грамотным. Зачем вызывать много sed'ов, если это можно сделать один раз?
sed 's/ /0/g; s/\([0-9][0-9][0-9][0-9]\)/\1 /g; s/\.$//'

Оффлайн Speccyfighter

  • Мастер
  • ***
  • Сообщений: 10 259
Re: Правильный конвертер hex2bin
« Ответ #2 : 26.08.2021 14:50:25 »
sed 's/ /0/g' | sed 's/\([0-9][0-9][0-9][0-9]\)/\1 /g' | sed 's/\.$//'
Это не выглядит грамотным. Зачем вызывать много sed'ов, если это можно сделать один раз?
sed 's/ /0/g; s/\([0-9][0-9][0-9][0-9]\)/\1 /g; s/\.$//'

Правильно, с точки зрения корректного вывода. А конструкция, это уже дело десятое.
Неправильно и ламерство, это когда в принципе неверный код, с в принципе неверным выводом, релизится и интегрируется во все линукс-системы. И программист не мог об этом не знать (по крайней мере не должен был не знать этих базовых основ), если формат идёт на стандартный вывод:
# sfdisk -T | head -n 19
Id  Имя

 0  Empty
 1  FAT12
 2  XENIX root
 3  XENIX usr
 4  FAT16 <32M
 5  Extended
 6  FAT16
 7  HPFS/NTFS/exFAT
 8  AIX
 9  AIX bootable
 a  OS/2 Boot Manager
 b  W95 FAT32
 c  W95 FAT32 (LBA)
 e  W95 FAT16 (LBA)
 f  W95 Ext'd (LBA)
10  OPUS
11  Hidden FAT12

На фоне этого ^^^^^ косяка, который бьёт по глазам, несколько sed-ов не кажутся большой ошибкой.

Оффлайн Speccyfighter

  • Мастер
  • ***
  • Сообщений: 10 259
Re: Правильный конвертер hex2bin
« Ответ #3 : 26.08.2021 15:33:14 »
Подправленный шелл-код оригинала:

...
и h2b-human()
h2b-human(){
[ -z "$1" -o "$1" = "-h" ] && echo "h2b: Converts HEX to binary" && return 0
INPUT=`echo "${@^^}" | sed 's/^0X//'`  # uppercase
LENGTH=`echo $INPUT | wc -c`
printf "%$(($LENGTH * 4 - 4))s\n" "$(echo "ibase=16; obase=2; $INPUT" | bc)" | sed 's/ /0/g' | sed 's/\([0-9][0-9][0-9][0-9]\)/\1 /g' | sed 's/\.$//'
}
$ h2b-human 010a010a
0000 0001 0000 1010 0000 0001 0000 1010

Ага, щас.
Межполубайтовые заменил. А после последнего полубайта, не?, - ну "молодец".
Моя ошибка:
межполубайтовые заменил на пробел, а удаление пробела после последнего полубайта не поправил:
$ h2b-human 01
0000 0001
$ h2b-human 01 | wc -c
11
$ h2b-human 01 | hexdump -C
00000000  30 30 30 30 20 30 30 30  31 20 0a                 |0000 0001 .|
0000000b

Правильно:
$ grep h2b-human -A5 .bashrc
h2b-human(){
[ -z "$1" -o "$1" = "-h" ] && echo "h2b: Converts HEX to binary" && return 0
INPUT=`echo "${@^^}" | sed 's/^0X//'`  # uppercase
LENGTH=`echo $INPUT | wc -c`
printf "%$(($LENGTH * 4 - 4))s\n" "$(echo "ibase=16; obase=2; $INPUT" | bc)" | sed 's/ /0/g' | sed 's/\([0-9][0-9][0-9][0-9]\)/\1 /g' | sed 's/\ $//'
}
$ h2b-human 01
0000 0001
$ h2b-human 01 | wc -c
10
$ h2b-human 01 | hexdump -C
00000000  30 30 30 30 20 30 30 30  31 0a                    |0000 0001.|
0000000a

Для h2b-human это конечно не критично, но всё же.

С h2b-array всё нормально:
$ h2b-array 01
00000001
$ h2b-array 01 | wc -c
9
$ h2b-array 01 | hexdump -C
00000000  30 30 30 30 30 30 30 31  0a                       |00000001.|
00000009
$ h2b-array 01 | wc -L # бит
8

Оффлайн Speccyfighter

  • Мастер
  • ***
  • Сообщений: 10 259
Re: Правильный конвертер hex2bin
« Ответ #4 : 26.08.2021 17:44:39 »
Когда потерян полубайт:

Ага, щас, аж бегом:
# parse-edid < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid | grep DisplaySize
Checksum Correct

DisplaySize 340 190
# rpm -qf --qf '%{NAME}\n' $(which parse-edid)
read-edid

Размер по горизонтали в миллиметрах, это 12-тибитное число, состоящее из байта и старшего полубайта:
# hexdump -C -s 55 -n 16 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000037  1d 56 f4 50 00 16 30 30  20 35 00 58 c2 10 00 00  |.V.P..00 5.X....|
00000047
# hexdump -C -s 66 -n 1 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000042  58                                                |X|
00000043
# hexdump -C -s 68 -n 1 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000044  10                                                |.|
00000045
# sh ./base.sh 0x158
hex=158    dec=344    oct=530    bin=101011000

о котором спецификация говорит:
Horizontal Addressable Video Image Size in mm is represented by a 12 bit number (Upper
nibble of byte 14 and the 8 bits of byte 12) - Range is 0 mm to 4095 mm.
Vertical Addressable Video Image Size in mm is represented by a 12 bit number (Lower nibble
of byte 14 and the 8 bits of byte 13) - Range is 0 mm to 4095 mm.


А в parse-edid прощёлкали старший полубайт, у которого биты могут быть и в нуле.

Соответственно должно быть и для размера в миллиметрах по вертикали:
# hexdump -C -s 55 -n 16 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000037  1d 56 f4 50 00 16 30 30  20 35 00 58 c2 10 00 00  |.V.P..00 5.X....|
00000047
# hexdump -C -s 67 -n 1 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000043  c2                                                |.|
00000044
# hexdump -C -s 68 -n 1 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000044  10                                                |.|
00000045
# sh ./base.sh 0x0c2
hex=C2    dec=194    oct=302    bin=11000010

Будь он хоть в нуле, - терять полубайт нельзя.
read-edid нужно выкинуть. Это неправильный код.

Правильный код, это edid-decode.
Но и в нём есть деталь:
$ edid-decode < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid | grep 'image size'
Maximum image size: 34 cm x 19 cm

Спецификация говорит, что это не максимальный размер изображения:
# hexdump -C -s 21 -n 1 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000015  22                                                |"|
00000016
# sh ./base.sh 0x22
hex=22    dec=34    oct=42    bin=100010
# hexdump -C -s 22 -n 1 < /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/edid
00000016  13                                                |.|
00000017
# sh ./base.sh 0x13
hex=13    dec=19    oct=23    bin=10011

Это:
Horizontal Screen Size in cm.
Vertical Screen Size in cm.


(которое в зависимости от содержимого байтов, может быть и соотношением сторон, а не размером)