поменять местами байты в uint32_t
От: IROV..  
Дата: 23.03.15 20:11
Оценка:
есть

uint32_t color = 0xFFFF00AA;


нужно поменять FF и AA местами

как правильней делать?
преобразовать к uint8_t * и свапнуть 0 и 2 элемент.
либо делать

uint8_t a = (color >> 24) & 0xFF;
uint8_t b = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t r = (color >> 0) & 0xFF;

uint32_t p_color = (a << 24) + (r << 16) + (g << 8) + (b << 0);


для x84 и для ARM и вообще для мобильных и тд

З.Ы. кроме big endian проблемы
я не волшебник, я только учусь!
Отредактировано 23.03.2015 20:13 IROV.. . Предыдущая версия .
Re: поменять местами байты в uint32_t
От: Erop Россия  
Дата: 23.03.15 21:35
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>как правильней делать?

IRO>преобразовать к uint8_t * и свапнуть 0 и 2 элемент.
Это вряд ли хорошо, будут какие-нибудь пенальти за смены разрядности и работу с памятью...


Я бы по маске вырезал "неподвижную и "подвижную" части, и подвижной сделал бы rotl какой-нибудь интрисик-функцией...
А если компиллер интрисики не умеет эффективно, то что-то вроде
(x&0xFF00FF00)|((x&0x00FF0000)>>16)|((x&0x000000FF)<<16)
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: поменять местами байты в uint32_t
От: Andrew S Россия http://alchemy-lab.com
Дата: 23.03.15 22:01
Оценка: +1 -1
IRO>есть

IRO>
IRO>uint32_t color = 0xFFFF00AA;
IRO>


IRO>нужно поменять FF и AA местами


IRO>как правильней делать?

IRO>преобразовать к uint8_t * и свапнуть 0 и 2 элемент.

Да, так. Можно еще попробовать битфилды запользовать — но тут надо смотреть реально генерируемый код.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[2]: поменять местами байты в uint32_t
От: IROV..  
Дата: 24.03.15 09:16
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, IROV.., Вы писали:


E>Это вряд ли хорошо, будут какие-нибудь пенальти за смены разрядности и работу с памятью...

А вот тут по подробней?


E>Я бы по маске вырезал "неподвижную и "подвижную" части, и подвижной сделал бы rotl какой-нибудь интрисик-функцией...

E>А если компиллер интрисики не умеет эффективно, то что-то вроде
(x&0xFF00FF00)|((x&0x00FF0000)>>16)|((x&0x000000FF)<<16)

ну просто формулой понятно, что можно про-оптимизировать. Но тут вопрос в подходах.

Можно ли потрошить uint32_t и играть с указателями на байты. Либо делать это мат — апаратом
я не волшебник, я только учусь!
Re: поменять местами байты в uint32_t
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 24.03.15 09:34
Оценка: +1
Здравствуйте, IROV.., Вы писали:

IRO>есть


IRO>
IRO>uint32_t color = 0xFFFF00AA;
IRO>


IRO>нужно поменять FF и AA местами

union можно попробовать использовать для хранения цвета.
Sic luceat lux!
Re: поменять местами байты в uint32_t
От: Pzz Россия https://github.com/alexpevzner
Дата: 24.03.15 09:42
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>для x84 и для ARM и вообще для мобильных и тд


Ну, если хочется портобабельно, то сдвигами и масками. В надежде, что компилятор поймет, что вы пытаетесь сказать, и подберет более-менее подходящий ассемблерный вариант (например, gcc догадывается циклический сдвиг, расписанный сдвигами и масками, в команду ror/rol превратить).

А так, на любой платформе что-нибудь да есть, что превращается в нормальный ассемблер. Например, htonl/ntohl, если речь идет о преобразовании между нативным форматом и big endian. Нп универсального портабельного варианта нет, к сожалению.
Re: поменять местами байты в uint32_t
От: Кодт Россия  
Дата: 24.03.15 09:45
Оценка: +1
Здравствуйте, IROV.., Вы писали:

IRO>как правильней делать?

IRO>для x84 и для ARM и вообще для мобильных и тд

Для дебажной версии — без разницы, лишь бы работало; для релизной — сделать платформо-зависимый код, наиболее эффективно решающий эту задачу (надо читать документацию на процессор и компилятор, затем профилировать).

Компилятору удобнее будет, если арифметика останется арифметикой — меньше шансов на пессимизацию, чем при манёврах с битфилдами и байтовой адресацией.
constexpr uint32_t rgba_to_bgra(uint32_t rgba)
{
  return (rgba & 0x00FF00FF) | (rgba & 0xFF000000 >> 16) | (rgba & 0x0000FF00 << 16);
}
Перекуём баги на фичи!
Re: поменять местами байты в uint32_t
От: aik Австралия  
Дата: 24.03.15 09:54
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>либо делать

IRO>
IRO>uint8_t a = (color >> 24) & 0xFF;
IRO>uint8_t b = (color >> 16) & 0xFF;
IRO>uint8_t g = (color >> 8) & 0xFF;
IRO>uint8_t r = (color >> 0) & 0xFF;
IRO>uint32_t p_color = (a << 24) + (r << 16) + (g << 8) + (b << 0);
IRO>


почти так. ядро линукса ( https://github.com/torvalds/linux/blob/master/include/uapi/linux/swab.h ) делает так в простом случае (в сложном умеет подставить правильную ассемблерную инструкцию, которая пишет/читает регистры в память сразу с перестановкой байтов):
#define ___constant_swab32(x) ((__u32)(                         \
        (((__u32)(x) & (__u32)0x000000ffUL) << 24) |            \
        (((__u32)(x) & (__u32)0x0000ff00UL) <<  8) |            \
        (((__u32)(x) & (__u32)0x00ff0000UL) >>  8) |            \
        (((__u32)(x) & (__u32)0xff000000UL) >> 24)))


компилятор такое переваривает хорошо. с типами, "UL" и "|" вместо "+".

IRO>для x84 и для ARM и вообще для мобильных и тд


они ж все little-endian, нет разве? ну, если "x84" на самом деле "x86"

IRO>З.Ы. кроме big endian проблемы


это что за проблема конкретно?
Re[2]: поменять местами байты в uint32_t
От: IROV..  
Дата: 24.03.15 10:01
Оценка:
Здравствуйте, aik, Вы писали:

aik>Здравствуйте, IROV.., Вы писали:


IRO>>либо делать

IRO>>
IRO>>uint8_t a = (color >> 24) & 0xFF;
IRO>>uint8_t b = (color >> 16) & 0xFF;
IRO>>uint8_t g = (color >> 8) & 0xFF;
IRO>>uint8_t r = (color >> 0) & 0xFF;
IRO>>uint32_t p_color = (a << 24) + (r << 16) + (g << 8) + (b << 0);
IRO>>


aik>почти так. ядро линукса ( https://github.com/torvalds/linux/blob/master/include/uapi/linux/swab.h ) делает так в простом случае (в сложном умеет подставить правильную ассемблерную инструкцию, которая пишет/читает регистры в память сразу с перестановкой байтов):

aik>
aik>#define ___constant_swab32(x) ((__u32)(                         \
aik>        (((__u32)(x) & (__u32)0x000000ffUL) << 24) |            \
aik>        (((__u32)(x) & (__u32)0x0000ff00UL) <<  8) |            \
aik>        (((__u32)(x) & (__u32)0x00ff0000UL) >>  8) |            \
aik>        (((__u32)(x) & (__u32)0xff000000UL) >> 24)))             
aik>


aik>компилятор такое переваривает хорошо. с типами, "UL" и "|" вместо "+".

ок, я тоже вижу везде примерно такой код.
Но некоторые люди говорят что это "перевымудренно" и давай свапать байты по uint8_t *
у меня сразу "стоп-сигнал" из разряда работа с памятью, могут быть траблы.
На IOS и Android вроде есть выравнивание по 4 байта для указателя. Наловил этих дров.
Думал отсюда ноги ростут.

IRO>>для x84 и для ARM и вообще для мобильных и тд

aik>они ж все little-endian, нет разве? ну, если "x84" на самом деле "x86"
уже вроде нет) little-endian можно в реальной жизни найти на старых маках там где еще Power проц был, и да спасибо чтото уже x64 в голову въелся что 4 тащу везде))

IRO>>З.Ы. кроме big endian проблемы

aik>это что за проблема конкретно?
big-endian VS little-endian
я не волшебник, я только учусь!
Re: поменять местами байты в uint32_t
От: B0FEE664  
Дата: 24.03.15 10:01
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>
IRO>uint32_t color = 0xFFFF00AA;
IRO>

IRO>нужно поменять FF и AA местами

IRO>как правильней делать?

Так как порядок байтов нам не известен, то перед преобразованиями его надо привести к чему нибудь известному.
Я использую для этого htonl(..) и ntohl(..). Другой переносимый способ мне не известен. Дело в том, что (чисто теоретически) порядок может быть не только обратным, но и смешанным.

IRO>преобразовать к uint8_t * и свапнуть 0 и 2 элемент.

Я бы преобразовал, таки, к unsigned char*, а не к uint8_t*. В стандарте нет гарантии, что sizeof(uint8_t) == sizeof(char)
Кстати, нет гарантии, что и sizeof(uint32_t) == 4, так что вариант со сдвигами предпочтительнее, если интересует переносимость.

IRO>либо делать

Можно и так, но сначала htonl
IRO>
IRO>uint8_t a = (color >> 24) & 0xFF;
IRO>uint8_t b = (color >> 16) & 0xFF;
IRO>uint8_t g = (color >> 8) & 0xFF;
IRO>uint8_t r = (color >> 0) & 0xFF;

IRO>uint32_t p_color = (a << 24) + (r << 16) + (g << 8) + (b << 0);
IRO>

а здесь надо добавить ntohl

IRO>для x84 и для ARM и вообще для мобильных и тд

IRO>З.Ы. кроме big endian проблемы
Это связанные вещи.
И каждый день — без права на ошибку...
Re[3]: поменять местами байты в uint32_t
От: aik Австралия  
Дата: 24.03.15 10:07
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>>>для x84 и для ARM и вообще для мобильных и тд

aik>>они ж все little-endian, нет разве? ну, если "x84" на самом деле "x86"
IRO>уже вроде нет) little-endian можно в реальной жизни найти на старых маках там где еще Power проц был, и да спасибо чтото уже x64 в голову въелся что 4 тащу везде))

Все ровно наоборот — x86 всегда был (и будет) little-endian, и последние дистрибутивы линукса (типа fedora21, ubuntu14) для powerpc64 тоже собираются под little-endian. Я знаю что arm умеет и так, и эдак, но что у них по дефолту — не знаю.
Re[4]: поменять местами байты в uint32_t
От: IROV..  
Дата: 24.03.15 10:14
Оценка:
Здравствуйте, aik, Вы писали:

aik>Здравствуйте, IROV.., Вы писали:


IRO>>>>для x84 и для ARM и вообще для мобильных и тд

aik>>>они ж все little-endian, нет разве? ну, если "x84" на самом деле "x86"
IRO>>уже вроде нет) little-endian можно в реальной жизни найти на старых маках там где еще Power проц был, и да спасибо чтото уже x64 в голову въелся что 4 тащу везде))

aik>Все ровно наоборот — x86 всегда был (и будет) little-endian, и последние дистрибутивы линукса (типа fedora21, ubuntu14) для powerpc64 тоже собираются под little-endian. Я знаю что arm умеет и так, и эдак, но что у них по дефолту — не знаю.

ну да) это я уже в названиях перепутал. Просто опять же смысл в том что сейчас все привели к одному, пусть будет "little-endian")))
я не волшебник, я только учусь!
Re: поменять местами байты в uint32_t
От: Mr.Delphist  
Дата: 24.03.15 13:14
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>есть


IRO>
IRO>uint32_t color = 0xFFFF00AA;
IRO>


IRO>нужно поменять FF и AA местами


IRO>как правильней делать?


Всё уже придумано до нас, с учётом endianness и прочее. Взять в интернете/Линуксе/etc реализацию htonl/ntohl, убрать лишние байт-манипуляции Получаете свой макрос (или функцию, смотрите что удобнее).
Re[2]: поменять местами байты в uint32_t
От: IROV..  
Дата: 24.03.15 15:07
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Здравствуйте, IROV.., Вы писали:


IRO>>есть


IRO>>
IRO>>uint32_t color = 0xFFFF00AA;
IRO>>


IRO>>нужно поменять FF и AA местами


IRO>>как правильней делать?


MD>Всё уже придумано до нас, с учётом endianness и прочее. Взять в интернете/Линуксе/etc реализацию htonl/ntohl, убрать лишние байт-манипуляции Получаете свой макрос (или функцию, смотрите что удобнее).


А что против свап двух байт по указателю? Чем это хуже если не брать во внимание endianness
я не волшебник, я только учусь!
Re[3]: поменять местами байты в uint32_t
От: Mr.Delphist  
Дата: 24.03.15 15:53
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>А что против свап двух байт по указателю? Чем это хуже если не брать во внимание endianness


Ну, уже упоминалось, что
1) нужно знать смещения этих байтов относительно исходного адреса (зависит от endianness)
2) работа с non-aligned адресами может быть либо хуже с точки зрения перфоманса, либо просто невозможна на данной машинной архитектуре
Re[2]: поменять местами байты в uint32_t
От: andyp  
Дата: 24.03.15 16:05
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Я бы по маске вырезал "неподвижную и "подвижную" части, и подвижной сделал бы rotl какой-нибудь интрисик-функцией...

E>А если компиллер интрисики не умеет эффективно, то что-то вроде
(x&0xFF00FF00)|((x&0x00FF0000)>>16)|((x&0x000000FF)<<16)



Вспомнилась нетленка — Hacker's Delight:

Generalized Bit Reversal

[GLS1] suggests that the following sort of generalization of bit reversal, which he calls "flip," is a good candidate to consider for a computer's instruction set:

if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
(The last two and operations can be omitted.) For k = 31, this operation reverses the bits in a word. For k = 24, it reverses the bytes in a word. For k = 7, it reverses the bits in each byte, without changing the positions of the bytes. For k = 16, it swaps the left and right halfwords of a word, and so on. In general, it moves the bit at position m to position m xor k. It can be implemented in hardware very similarly to the way a rotate shifter is usually implemented (five stages of MUX's, with each stage controlled by a bit of the shift amount k).

Re[3]: поменять местами байты в uint32_t
От: Erop Россия  
Дата: 24.03.15 18:58
Оценка:
Здравствуйте, IROV.., Вы писали:


E>>Это вряд ли хорошо, будут какие-нибудь пенальти за смены разрядности и работу с памятью...

IRO>А вот тут по подробней?

Это зависит от конкретной архитектуры и от того где данные лежат до обмена байт и где должны оказаться после.
Если, например, работа с памятью медленная, то выгружать регистр в память, что бы потом переслать ещё байт туда/байт сюда, а потом загрузить данные из памяти обратно -- это тормоза, хотя есть надежда на кэш...
Если архитектура допускает параллельность, то опять же все эти выгрузки в память и загрузки обратно, а так же перходы между разной битностью, процу трудно параллелить, а вычисление формулы вроде (x&~mask)|__rotl(x&mask, 16) параллелится идеально... И результат сразу в процессоре имеем.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: поменять местами байты в uint32_t
От: watchmaker  
Дата: 24.03.15 20:06
Оценка: +1
Здравствуйте, IROV.., Вы писали:


IRO>для x84 и для ARM и вообще для мобильных

Если всё же отвлечься от С, а просто рассмотреть задачу по перетасовки байт на x86 и на ARM, то можно ещё вспомнить, что, например, у первого встречается SSE, а у второго — Neon. А в них есть есть инструкции, делающие именно нужные преобразования сразу над массивами байт. Тебе ведь порядок байт нужно не в одном пикселе единоразово поменять, а в целом массиве, не так ли?
Ну то есть, если нужно сделать чтобы работало быстро, то придётся учитывать возможности платформы (или, что лучше, взять готовую библиотеку, где нужные функции уже написаны и оптимизированы под все распространённые версии процессоров). А если использовать С, то, как уже сказали, используй побитовые сдвиги и маски, и надейся что компилятор сможет это более-менее векторизовать.


IRO>преобразовать к uint8_t * и свапнуть 0 и 2 элемент.

В контексте векторизации, кстати говоря, такая операция может смутить компилятор и векторизовать он может отказаться. Ну то есть помимо того, что на многих архитектурах сама манипуляция байтами выйдет дороже свапа внутри 32-битного регистра, так ещё и компилятор такой код будет хуже оптимизировать.
Re[2]: поменять местами байты в uint32_t
От: Andrew S Россия http://alchemy-lab.com
Дата: 25.03.15 00:21
Оценка: 4 (1)
IRO>>есть

IRO>>
IRO>>uint32_t color = 0xFFFF00AA;
IRO>>


IRO>>нужно поменять FF и AA местами


IRO>>как правильней делать?

IRO>>преобразовать к uint8_t * и свапнуть 0 и 2 элемент.

AS>Да, так. Можно еще попробовать битфилды запользовать — но тут надо смотреть реально генерируемый код.



IRO>>>есть


IRO>>>
IRO>>>uint32_t color = 0xFFFF00AA;
IRO>>>


IRO>>>нужно поменять FF и AA местами


IRO>>>как правильней делать?


MD>>Всё уже придумано до нас, с учётом endianness и прочее. Взять в интернете/Линуксе/etc реализацию htonl/ntohl, убрать лишние байт-манипуляции Получаете свой макрос (или функцию, смотрите что удобнее).


IRO>А что против свап двух байт по указателю? Чем это хуже если не брать во внимание endianness


Для интереса попробовал на VC8.

вариант
inline uint32_t color_swap4(uint32_t color)
{
    return ((color & 0xFF)  << 16) | ((color >> 16) & 0xFF) | (color & 0xFF00FF00);
}

00401057 8B 44 8C 40      mov         eax,dword ptr [esp+ecx*4+40h] 
0040105B 0F B6 D0         movzx       edx,al 
0040105E 8B E8            mov         ebp,eax 
00401060 C1 ED 10         shr         ebp,10h 
00401063 C1 E2 10         shl         edx,10h 
00401066 81 E5 FF 00 00 00 and         ebp,0FFh 
0040106C 0B D5            or          edx,ebp 
0040106E 25 00 FF 00 FF   and         eax,0FF00FF00h 
00401073 0B D0            or          edx,eax


по результирующему машинному коду эквивалентен

union color_t1
{
//    color_t(uint32_t v): v4(v)
    uint32_t v4;
    struct
    {
        uint32_t v1_1:8;
        uint32_t v1_2:8;
        uint32_t v1_3:8;
        uint32_t v1_4:8;
    };
};
inline uint32_t color_swap3(uint32_t color)
{
    uint8_t tmp = ((color_t1 &)color).v1_1;
    ((color_t1 &)color).v1_1 = ((color_t1 &)color).v1_3;
    ((color_t1 &)color).v1_3 = tmp;
    return color;
}


00401057 8B 44 8C 40      mov         eax,dword ptr [esp+ecx*4+40h] 
0040105B 8B D0            mov         edx,eax 
0040105D C1 EA 10         shr         edx,10h 
00401060 0F B6 E8         movzx       ebp,al 
00401063 81 E2 FF 00 00 00 and         edx,0FFh 
00401069 C1 E5 10         shl         ebp,10h 
0040106C 0B D5            or          edx,ebp 
0040106E 25 00 FF 00 FF   and         eax,0FF00FF00h 
00401073 0B D0            or          edx,eax


Вариант с кастом к массиву компилятор не может корректно оптимизировать в случае использования в качестве источника данных массива uin32_t — получается лишнее перекладывание на стек. В результае на моем железе вариант с битовыми операциями примерно в 8 раз быстрее и примерно равен по стоимости обвязке (вычитыванию значения из памяти + организация цикла). Получается, в данном случае надо использовать либо битовые операции в любом из вариантов выше, либо rol, если он есть.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[4]: поменять местами байты в uint32_t
От: Pavel Dvorkin Россия  
Дата: 25.03.15 03:49
Оценка: +1
Здравствуйте, Mr.Delphist, Вы писали:

MD>2) работа с non-aligned адресами может быть либо хуже с точки зрения перфоманса, либо просто невозможна на данной машинной архитектуре


О каком align может идти речь при работе с байтами ? Для них alignment вроде как всегда 1 ?
With best regards
Pavel Dvorkin
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.