Endianita: Porovnání verzí

Z Wikipedie, otevřené encyklopedie
Smazaný obsah Přidaný obsah
pridani Apple M-series cipu mezi uzivatele little endian
 
(Není zobrazeno 46 mezilehlých verzí od 32 dalších uživatelů.)
Řádek 1: Řádek 1:
'''Endianita''' je v [[informatika (počítačová věda)|informatice]] způsob uložení čísel v [[elektronická paměť|paměti]] [[počítač]]e, který definuje, v jakém pořadí se uloží jednotlivé [[bajt]]y číselného [[datový typ|datového typu]]. Označuje se také jako '''pořadí bajtů''' (anglicky ''byte order'').
'''Endianita''' ('''pořadí bajtů''', {{Vjazyce2|en|''byte order''}}) je v [[Informatika|informatice]] způsob uložení čísel v [[Operační paměť|operační paměti]] [[počítač]]e, který definuje, v jakém pořadí se uloží jednotlivé [[bajt]]y číselného [[datový typ|datového typu]]. Jde tedy o to, v jakém pořadí jsou v operační paměti uloženy jednotlivé řády čísel, které zabírají více než jeden bajt.


== Endianita a kompatibilita ==
== Endianita a kompatibilita ==

Endianita je jedním ze základních zdrojů nekompatibility při ukládání a výměně dat v digitální podobě. Je nutné brát ji v úvahu při přenášení binárních [[soubor]]ů nebo při [[počítačová síť|síťové]] komunikaci mezi různými platformami. Tento problém pramení z toho, že stejný zdrojový kód zkompilovaný pro počítače s různými procesory může kvůli jejich různé endianitě produkovat při ukládání nebo přenosu různá binární data. Nejrozšířenějším kódováním vícebajtových dat je v současnosti little endian, což je dané masovým rozšířením architektury [[x86|Intel x86]].
Endianita je jedním ze základních zdrojů nekompatibility při ukládání a výměně dat v digitální podobě. Je nutné brát ji v úvahu při přenášení binárních [[soubor]]ů nebo při [[počítačová síť|síťové]] komunikaci mezi různými platformami. Tento problém pramení z toho, že stejný zdrojový kód zkompilovaný pro počítače s různými procesory může kvůli jejich různé endianitě produkovat při ukládání nebo přenosu různá binární data. Nejrozšířenějším kódováním vícebajtových dat je v současnosti little endian, což je dané masovým rozšířením architektury [[x86|Intel x86]].


Řádek 10: Řádek 9:


== Little-endian ==
== Little-endian ==
V tomto případě se na paměťové místo s nejnižší adresou uloží [[nejméně významný bit]] (''LSB'') a za něj se ukládají ostatní bajty až po [[nejvíce významný bit]] (''MSB''). Architektury uplatňující tento princip se nazývají '''little-endian''' ([[mnemotechnická pomůcka]]: ''little end first'') a patří mezi ně [[MOS Technology 6502]], [[x86|Intel x86]] a DEC [[VAX]].
V tomto případě se na paměťové místo s nejnižší adresou uloží [[nejméně významný bajt]] (''LSB'') a za něj se ukládají ostatní bajty až po [[nejvíce významný bajt]] (''MSB''). Architektury uplatňující tento princip se nazývají '''little-endian''' ([[mnemotechnická pomůcka]]: ''little end first'') a patří mezi ně [[MOS Technology 6502]], [[x86|Intel x86]], [[Apple M1]]<ref>{{Citace elektronického periodika
| titul = Porting Your macOS Apps to Apple Silicon

| periodikum = Apple Developer Documentation
Little endian má jednu dobrou vlastnost. Jedna a ta samá hodnota může být z paměti načtena pro různou délku, bez změny adresy. Například 32 bitový řetězec FF 00 00 00 může být načten ze stejné adresy jako 8 bitový (hodnota = FF), 16bitový (00FF), 24bitový (0000FF), 32bitový (000000FF); jejich hodnota stále zůstává 255. Tato vlastnost je však velmi zřídka využívána programátory, kteří pracují s vyššími programovacími jazyky, proto se ponechává kompilátoru.
| url = https://docs.developer.apple.com/documentation/apple-silicon/porting-your-macos-apps-to-apple-silicon
| jazyk = en-US
| datum přístupu = 2023-12-19
}}</ref> a DEC [[VAX]].


Little endian má jednu dobrou vlastnost. Jedna a ta samá hodnota může být z paměti načtena pro různou délku, bez změny adresy. Například 32bitový řetězec FF 00 00 00 může být načten ze stejné adresy jako 8bitový (hodnota = FF), 16bitový (00FF), 24bitový (0000FF), 32bitový (000000FF); jejich hodnota stále zůstává 255. Tato vlastnost je však velmi zřídka využívána programátory, kteří pracují s vyššími programovacími jazyky, proto se ponechává kompilátoru.


Např. 32[[bit]]ové číslo <code>0x4A3B2C1D</code> se na adresu <code>100</code> uloží takto:
Např. 32[[bit]]ové číslo <code>0x4A3B2C1D</code> se na adresu <code>100</code> uloží takto:
Řádek 34: Řádek 38:


== Big-endian ==
== Big-endian ==
V tomto případě se na paměťové místo s nejnižší adresou uloží [[nejvíce významný bajt]] (''MSB'') a za něj se ukládají ostatní bajty až po [[nejméně významný bajt]] (''LSB'') na konci. Architektury uplatňující tento princip se nazývají '''big-endian''' ([[mnemotechnická pomůcka]]: ''big end first'') a patří mezi ně [[Motorola 68000]], [[SPARC]] a [[System/370]].
V tomto případě se na paměťové místo s nejnižší adresou uloží [[nejvíce významný bajt]] (''MSB'') a za něj se ukládají ostatní bajty až po [[nejméně významný bajt]] (''LSB'') na konci. Architektury uplatňující tento princip se nazývají '''big-endian''' ([[mnemotechnická pomůcka]]: ''big end first'') a patří mezi ně [[Motorola 68000]], [[SPARC]] a [[System/370]].


Např. 32[[bit]]ové číslo <code>0x4A3B2C1D</code> se na adresu <code>100</code> uloží takto:
Např. 32[[bit]]ové číslo <code>0x4A3B2C1D</code> se na adresu <code>100</code> uloží takto:
Řádek 55: Řádek 59:


== Middle-endian ==
== Middle-endian ==
Některé architektury označované '''middle-endian''' (nebo někdy '''mixed-endian''') užívají složitější způsob pro určení pořadí jednotlivých bajtů, který je dán kombinací obou výše zmíněných způsobů. Mezi takovéto architektury patří např. rodina procesorů [[PDP-11]]. Tento formát je také použit pro ukládání čísel s pohyblivou řádovou čárkou a dvojitou přesností v systémech VAX a [[ARM]].
Některé architektury označované '''middle-endian''' (nebo někdy '''mixed-endian''') užívají složitější způsob pro určení pořadí jednotlivých bajtů, který je dán kombinací obou výše zmíněných způsobů. Mezi takovéto architektury patří např. rodina procesorů [[PDP-11]]. Tento formát je také použit pro ukládání čísel s pohyblivou řádovou čárkou a dvojitou přesností v systémech VAX a [[ARM]].


Např. 32[[bit]]ové číslo <code>0x4A3B2C1D</code> se na adresu <code>100</code> uloží takto:
Např. 32[[bit]]ové číslo <code>0x4A3B2C1D</code> se na adresu <code>100</code> uloží takto:
Řádek 95: Řádek 99:


== Endianity v souborech ==
== Endianity v souborech ==
Pokud je binární soubor vytvořen a následně čten na počítačích, které mají různou endianitu, může vzniknout problém. Některé [[překladač]]e mají vestavěná zařízení, která pracují s údaji zapsanými v jiných formátech. Například [[kompilátor]] Intel Fortran podporuje nestandardní <code>CONVERT</code> specifikátor, takže soubor lze otevřít jako:
Pokud je binární soubor vytvořen a následně čten na počítačích, které mají různou endianitu, může vzniknout problém. Některé [[překladač]]e mají vestavěná zařízení, která pracují s údaji zapsanými v jiných formátech. Například [[překladač|kompilátor]] Intel Fortran podporuje nestandardní <code>CONVERT</code> specifikátor, takže soubor lze otevřít jako:


OPEN (unit, CONVERT = 'BIG_ENDIAN',...)
OPEN (unit, CONVERT = 'BIG_ENDIAN',...)
Řádek 103: Řádek 107:
OPEN (unit, CONVERT = 'LITTLE_ENDIAN',...)
OPEN (unit, CONVERT = 'LITTLE_ENDIAN',...)


Pokud kompilátor převod nepodporuje, musí záměnu bajtů provést programátor. Neformátované sekvenční soubory v jazyce [[Fortran]] vytvořené s jednou endianitou obvykle není možné číst na systému pomocí jiné endianity. [[Fortran]] obvykle provádí záznam (napsán jediným příkazem [[Fortran]]u) jako data a pole. Ta jsou rovna celočíselným bytům v datech. Pokus o čtení těchto souborů v systému s jinými endianitami má pak za následek provozní chybu, protože pole počítače jsou nesprávná. Tomuto problému se lze vyhnout tím, že píšeme přímo do sekvenčního binárního souboru.
Pokud kompilátor převod nepodporuje, musí záměnu bajtů (angl. ''byte swap'') provést programátor. Neformátované sekvenční soubory v jazyce [[Fortran]] vytvořené s jednou endianitou obvykle není možné číst na systému pomocí jiné endianity. [[Fortran]] obvykle provádí záznam (napsán jediným příkazem [[Fortran]]u) jako data a pole. Ta jsou rovna celočíselným bytům v datech. Pokus o čtení těchto souborů v systému s jinými endianitami má pak za následek provozní chybu, protože pole počítače jsou nesprávná. Tomuto problému se lze vyhnout tím, že píšeme přímo do sekvenčního binárního souboru.


Aplikace formátující binární data, jako je například [[MATLAB]] .mat soubory, nebo formát dat BIL, používané v [[topografie|topografii]], jsou obvykle nezávislé na endianitách.
Aplikace formátující binární data, jako je například [[MATLAB]] .mat soubory, nebo formát dat BIL, používané v [[topografie|topografii]], jsou obvykle nezávislé na endianitách.


Tohoto je dosaženo uložením dat vždy v jedné pevné endianitě nebo když spolu s údaji neseme [[switch]], který určí s kterou endianitou byla data zapsána. Při čtení souboru převede aplikace endianity. Tento postup je pro uživatele transparentní.
Tohoto je dosaženo uložením dat vždy v jedné pevné endianitě nebo když spolu s údaji neseme příznak, který určí, s kterou endianitou byla data zapsána. Při čtení souboru převede aplikace endianity, je-li třeba. Tento postup je pro uživatele transparentní.


To je případ obrazových souborů [[TIFF]], které informují ve svém záhlaví o endianitě použitých čísel. Pokud soubor začíná podpisem "MM", znamená to, že celá čísla jsou reprezentována jako velký endian, zatímco "II" endian malý. Tyto podpisy potřebuje každé jednotlivé 16bitové slovo. Jsou-li typu [[palindrom]] (to znamená, že se čtou stejně dopředu a dozadu), tak jsou nezávislé endianitách. "I" znamená [[Intel]] a "M" znamená [[Motorola]]. Procesory [[Intel]] používají malý endian, zatímco procesory [[Motorola]] 680x0 velký endian. Tento explicitní podpis umožňuje čtecímu programu obrazových souborů [[TIFF]] výměnu bytů. To nastane pouze v případě, když byl daný soubor vygenerován zapisovacím programem [[TIFF]] běžícím na počítači s různými endianitami.
To je případ obrazových souborů [[Tagged Image File Format|TIFF]], které informují ve svém záhlaví o endianitě použitých čísel. Pokud soubor začíná signaturou "<code>MM</code>", znamená to, že celá čísla jsou reprezentována jako velký endian, zatímco "<code>II</code>" endian malý. Každá z těchto signatur zabere jediné 16bitové slovo. Jsou typu [[palindrom]] (to znamená, že se čtou stejně dopředu i dozadu), takže jsou nezávislé endianitách. "<code>I</code>" znamená [[Intel]] a "<code>M</code>" znamená [[Motorola]]. Procesory [[Intel]] používají malý endian, zatímco procesory [[Motorola]] 680x0 velký endian. Tento explicitní podpis umožňuje čtecímu programu obrazových souborů [[Tagged Image File Format|TIFF]] výměnu bytů, a to pouze v případě, byl-li daný soubor vygenerován zapisovacím programem [[Tagged Image File Format|TIFF]] běžícím na počítači s jinou endianitou.


I když je programovací prostředí LabVIEW nejčastěji instalováno na počítačích s operačním systémem Windows, bylo nejprve vyvinuto pro Macintosh. Formát velkého endianu je používán pro binární čísla, zatímco většina Windows používá malý endian.
I když je programovací prostředí LabVIEW nejčastěji instalováno na počítačích s operačním systémem Windows, bylo nejprve vyvinuto pro Macintosh. Formát velkého endianu je používán pro binární čísla, zatímco většina Windows používá malý endian.


Za povšimnutí stojí následující fakt: Jelikož potřebná výměna bajtů závisí na délce proměnných uložených v souboru, proto obecný nástroj pro přeměnu endianit v binárních souborech nemůže existovat. Dvoubajtová celá čísla vyžadují odlišný SWAP než čtyřbajtové celé číslo.
Za povšimnutí stojí fakt, že neexistuje obecný nástroj pro přeměnu endianit v souborech. Ke správnému převodu nutno znát strukturu souboru. Potřebná výměna bajtů závisí totiž na délce proměnných uložených v souboru (čtyřbajtové celé číslo vyžaduje jiný převod než dvojice dvoubajtových celých čísel).


== Programovaní ==
== Programování ==
=== Instrukce BSWAP ===
[[Strojová instrukce]] <code>bswap</code><ref>[http://web.itu.edu.tr/kesgin/mul06/intel/instr/bswap.html Intel Instruction Set (bswap)]</ref> slouží ke konverzi 32bitových hodnot z little-endian na big-endian a naopak. Tato instrukce je k dispozici pouze na platformách Intel počínaje řadou 80486 (včetně) dál (486+). Zápis (použití) je následující:


=== Konverze mezi little-endian a big-endian (jazyk C) ===
<source lang="asm">
Následující výpočty přehazují bajty z kódování little-endian na big-endian a naopak. Použití standardního C-jazyka vede k lepší přenositelnosti kódu. Zpracování pomocí strojových instrukcí je rychlejší, což může hrát roli při zpracování velkých souborů dat, nebo velkých datových toků. Syntaxe zápisu je provedena na způsob maker v [[C (programovací jazyk)|jazyce C]].
BSWAP reg32
</source>


Aby se konverze provedla správně, musíme vědět jaký počet bajtů používá daný kompilátor C-jazyka pro určitý datový typ. Definice C-jazyka podle ANSI neurčuje, že např. typ <code>int</code> musí mít 32 bitů.
Bere právě jeden 32bitový operand a přehazuje první a druhý bajt se čtvrtým a třetím. Pokud by se instrukci předal 16bitový operand, je výsledek této instrukce nedefinovaný.

=== GCC ===
Tuto instrukci je možno použít nepřímo voláním speciálního konstruktu překladače [[GCC]] (pro jazyk C/C++) od verze 4.3. Jedná se o vestavěnou (built-in) funkci, proto není potřeba vkládat žádný hlavičkový soubor, ani přilinkovat žádnou knihovnu (GCC místo nich vkládá přímo instrukce). Výhodou těchto funkcí je, že pokud daná platforma nemá instrukce pro bitovou konverzi mezi little-endian a big-endinan, vloží místo nich optimalizovaný kód.

==== Prototyp pro 32bitový swap ====
<source lang="C">
int32_t __builtin_bswap32 (int32_t x);
</source>

Vrací 32bitovou hodnotu, která obsahuje přehozenou hodnotu 32bitové proměnné <code>x</code> (0x11223344 → 0x44332211).

==== Prototyp pro 64bitový swap ====
<source lang="C">
int64_t __builtin_bswap64 (int64_t x);
</source>

Podobně jako <code>__builtin_bswap32</code>, ale pracuje s 64bitovými hodnotami (jak parametr, tak i návratová hodnota).

Pro 16bitovou konverzi neexistuje vestavěná (built-in) funkce, ale jelikož se jedná o pouhé prohození dvou bajtů, stačí použít rotaci (viz výpočet níže).

=== Konverze mezi little-endian a big-endian (jazyk C) ===
Následující výpočty přehazují bajty z kódování little-endian na big-endian a naopak. Pokud je to možné, měly by se upřednostňovat strojové instrukce, protože je jejich zpracování rychlejší. Následující výpočty jsou na druhou stranu portabilní. Syntaxe zápisu je provedena na způsob maker v [[C (programovací jazyk)|jazyce C]].


==== 16bitový swap ====
==== 16bitový swap ====
<source lang="C">
<syntaxhighlight lang="C">
#define BSWAP16(n) ((n) << 8 | (n) >> 8)
#define BSWAP16(n) ((n) << 8 | ((n) >> 8 & 0x00FF))
</syntaxhighlight>
</source>


==== 32bitový swap ====
==== 32bitový swap ====
<source lang="C">
<syntaxhighlight lang="c">
#define BSWAP32(n) ((n) >> 24) | (((n) << 8) & 0x00FF0000) | (((n) >> 8) & 0x0000FF00) | ((n) << 24)
#define BSWAP32(n) (((n) & 0xFF000000L >> 24) | ((n) & 0x00FF0000L >> 8) | ((n) & 0x0000FF00L << 8) | ((n) & 0x000000FF << 24))
</syntaxhighlight>
</source>


==== 64bitový swap ====
==== 64bitový swap ====
<source lang="C">
<syntaxhighlight lang="C">
#define BSWAP64(n) ((n) >> 56) | (((n) << 40) & 0x00FF000000000000) | \
#define BSWAP64(n) ((n) >> 56) | (((n) << 40) & 0x00FF000000000000LL) | \
(((n) << 24) & 0x0000FF0000000000) | \
(((n) << 24) & 0x0000FF0000000000LL) | \
(((n) << 8) & 0x000000FF00000000) | \
(((n) << 8) & 0x000000FF00000000LL) | \
(((n) >> 8) & 0x00000000FF000000) | \
(((n) >> 8) & 0x00000000FF000000LL) | \
(((n) >> 24) & 0x0000000000FF0000) | \
(((n) >> 24) & 0x0000000000FF0000LL) | \
(((n) >> 40) & 0x000000000000FF00) | \
(((n) >> 40) & 0x000000000000FF00LL) | \
((n) << 56)
((n) << 56)
</syntaxhighlight>
</source>


=== Použití struktury union ===
==== Použití struktury union ====
Následující kód demonstruje "přehazování" bajtů 32bitového datového typu. Obecně jde o poměrně neefektivní způsob konverze endianity, ale např. při smíšené endianitě by může být názornost tohoto postupu výhodou.
Následujícím způsobem lze přehazovat bajty libovolně dlouhého datového typu. Daň, kterou si tato obecnost bere, je rychlost. Proto je daleko vhodnější použít některou z výše uvedených možností (instrukce, výpočet).
Další problém, který může nastat, je, že musíme zajistit přesný počet bajtů daného datového typu. Není definované, že např. <code>int</code> musí mít přesný počet bitů (je definována pouze jeho dolní hranice).<ref>[http://en.wikipedia.org/wiki/Integer_%28computer_science%29 Integer computer science]</ref><ref>[http://en.wikipedia.org/wiki/Limits.h limits.h]</ref>


<source lang="C">
<syntaxhighlight lang="C">
int32_t BSWAP32(int32_t data)
int32_t BSWAP32(int32_t data)
{
{
Řádek 189: Řádek 167:
return lf.val;
return lf.val;
}
}
</syntaxhighlight>
</source>

=== Architektura 80486 a odvozené ===
Tato instrukce je k dispozici pouze na platformách Intel počínaje řadou 80486 (včetně) dál (486+) existuje pro konverzi endianity [[strojová instrukce]] <code>bswap</code><ref>[http://web.itu.edu.tr/kesgin/mul06/intel/instr/bswap.html Intel Instruction Set (bswap)]</ref>. Tato instrukce slouží ke konverzi 32bitových nebo 64bitových hodnot z little-endian na big-endian a naopak. Zápis (použití) je následující:

<syntaxhighlight lang="asm">
BSWAP reg32
BSWAP reg64
</syntaxhighlight>

Bere buď 32bitový nebo 64bitový registr. Pokud by se použil se 16bitovým registrem, zanechá jej beze změny a nic se neprovede. Pro 16bitový operand a změnu endianity lze na procesorech x86 provést instrukce XCHG, např.:

<syntaxhighlight lang="asm">
XCHG al, ah
</syntaxhighlight>

==== Použití instrukce BSWAP v GCC ====
Tuto instrukci je možno použít nepřímo voláním speciálního konstruktu překladače [[GCC]] (pro jazyk C/C++) od verze 4.3. Jedná se o vestavěnou (built-in) funkci, proto není potřeba vkládat žádný hlavičkový soubor, ani přilinkovat žádnou knihovnu (GCC místo nich vkládá přímo instrukce). Výhodou těchto funkcí je, že pokud daná platforma nemá instrukce pro bitovou konverzi mezi little-endian a big-endinan, vloží místo nich optimalizovaný kód.

===== Prototyp pro 32bitový swap =====
<syntaxhighlight lang="C">
int32_t __builtin_bswap32 (int32_t x);
</syntaxhighlight>

Vrací 32bitovou hodnotu, která obsahuje přehozenou hodnotu 32bitové proměnné <code>x</code> (0x11223344 → 0x44332211).

===== Prototyp pro 64bitový swap =====
<syntaxhighlight lang="C">
int64_t __builtin_bswap64 (int64_t x);
</syntaxhighlight>

Podobně jako <code>__builtin_bswap32</code>, ale pracuje s 64bitovými hodnotami (jak parametr, tak i návratová hodnota).

Pro 16bitovou konverzi neexistuje v GCC vestavěná (built-in) funkce.


=== Visual C++ ===
=== Visual C++ ===
V jazyce [[C++]] je potřeba vložit hlavičkový soubor </code>intrin.h</code>.
V jazyce [[C++]] je potřeba vložit hlavičkový soubor <code>intrin.h</code>.


==== 16bitový swap ====
==== 16bitový swap ====
<source lang="C">
<syntaxhighlight lang="C">
unsigned short _byteswap_ushort(unsigned short value);
unsigned short _byteswap_ushort(unsigned short value);
</syntaxhighlight>
</source>


==== 32bitový swap ====
==== 32bitový swap ====
<source lang="C">
<syntaxhighlight lang="C">
unsigned long _byteswap_ulong(unsigned long value);
unsigned long _byteswap_ulong(unsigned long value);
</syntaxhighlight>
</source>


==== 64bitový swap ====
==== 64bitový swap ====
<source lang="C">
<syntaxhighlight lang="C">
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
</syntaxhighlight>
</source>


=== Detekce ===
=== Detekce ===
Řádek 215: Řádek 226:
Detekce při překladu by měla být preferována, protože se provede pouze jednou a výsledný program je tak jako tak závislý na dané platformě, kde byl překompilován. Ve většině [[UN*X|unixových systémů]] je k dispozici [[hlavičkový soubor]] <code>sys/param.h</code>, který kromě jiného poskytuje informace o pořadí bajtů (byte order). Definuje makra <code>__BYTE_ORDER</code>, <code>__LITTLE_ENDIAN, __BIG_ENDIAN</code>, a další, která lze využít např. následovně:
Detekce při překladu by měla být preferována, protože se provede pouze jednou a výsledný program je tak jako tak závislý na dané platformě, kde byl překompilován. Ve většině [[UN*X|unixových systémů]] je k dispozici [[hlavičkový soubor]] <code>sys/param.h</code>, který kromě jiného poskytuje informace o pořadí bajtů (byte order). Definuje makra <code>__BYTE_ORDER</code>, <code>__LITTLE_ENDIAN, __BIG_ENDIAN</code>, a další, která lze využít např. následovně:


<source lang="C">
<syntaxhighlight lang="C">
#include <sys/param.h>
#include <sys/param.h>


Řádek 227: Řádek 238:
# endif
# endif
#endif /* __BYTE_ORDER */
#endif /* __BYTE_ORDER */
</syntaxhighlight>
</source>


Dalším možným přístupem je rozhodnutí na základě znalosti endianity příslušné platformy. To může být komplikované, protože se jména maker (symbolických konstant) mohou lišit v závislosti na použitém překladači a zároveň nemusí být možné podchytit všechny platformy (včetně hybridních architektur).
Dalším možným přístupem je rozhodnutí na základě znalosti endianity příslušné platformy. To může být komplikované, protože se jména maker (symbolických konstant) mohou lišit v závislosti na použitém překladači a zároveň nemusí být možné podchytit všechny platformy (včetně hybridních architektur).


<source lang="C">
<syntaxhighlight lang="C">
#if defined (i386) || defined (__i386__) || defined (__alpha) || defined (vax)
#if defined (i386) || defined (__i386__) || defined (__alpha) || defined (vax)
# define ENDIAN_DEFAULT ENDIAN_LITTLE
# define ENDIAN_DEFAULT ENDIAN_LITTLE
Řádek 237: Řádek 248:
# define ENDIAN_DEFAULT ENDIAN_BIG
# define ENDIAN_DEFAULT ENDIAN_BIG
#endif
#endif
</syntaxhighlight>
</source>


==== Za běhu ====
==== Za běhu ====
Pokud nejsou k dispozici žádná makra, je možné použít výpočet za běhu programu. Obě následující funkce vrací 1, když je platforma big-endian, jinak vrací 0.
Pokud nejsou k dispozici žádná makra, je možné použít výpočet za běhu programu. Obě následující funkce vrací 1, když je platforma big-endian, jinak vrací 0.


<source lang="C">
<syntaxhighlight lang="C">
long int big_endian()
int is_big_endian()
{
{
long int i = 1;
static const int i = 1;
return !(*((char *) &i));
return *((char *)&i) == 0;
}
}
</syntaxhighlight>
</source>


Ve výše uvedeném kódu je do proměnné <code>i</code> (která 32 bitů)<ref>[http://en.wikipedia.org/wiki/Limits.h limits.h]</ref> uložena hodnota 1. V závislosti na dané platformě (endianitě), se číslo uloží bud jako 0x00000001 (big-endian) nebo jako 0x01000000 (little-endian) a pouze se kontroluje, jakou hodnotu obsahuje první bajt proměnné <code>i</code>.
Ve výše uvedeném kódu je do proměnné <code>i</code> (u které předpokládáme délku 32 bitů) uložena hodnota 1. V závislosti na endianitě dané platformy, se číslo uloží buď jako posloupnost bajtů <code>{0x00,0x00,0x00,0x01}</code> (big-endian) nebo jako posloupnost bajtů <code>{0x01,0x00,0x00,x00}</code> (little-endian). Funkce is_big_endian pouze kontroluje, jakou hodnotu obsahuje první bajt proměnné <code>i</code>.


===== Použití struktury union (Harbison and Steele) =====
===== Použití struktury union (Harbison and Steele) =====
Příklad použití struktury union:<ref>''C: A Reference Manual.'' Samuel P. Harbison and Guy L. Steele, Jr. Third edition; Prentice-Hall, 1991, ISBN 0-13-110933-2</ref>
Příklad použití struktury union:<ref>''C: A Reference Manual.'' Samuel P. Harbison and Guy L. Steele, Jr. Third edition; Prentice-Hall, 1991, {{ISBN|0-13-110933-2}}</ref>
<!-- (Tento kód jsem výrazně zefektivnil oproti originálu – Miloslav Ponkrác) -->


<source lang="C">
<syntaxhighlight lang="C">
long int big_endian()
int is_big_endian()
{
{
union {
union test_union
{
long int i;
int i;
char b[sizeof(long int)];
} e;
char c;
e.i = 1;
};

return (e.b[sizeof(long int) - 1] == 1);
static const union test_union s_t = { 1 };
return s_t.c == 0;
}
}
</syntaxhighlight>
</source>


== Reference ==
== Reference ==
Řádek 271: Řádek 285:


== Externí odkazy ==
== Externí odkazy ==
* {{commonscat}}
* [http://unixpapa.com/incnote/byteorder.html Unix Incompatibility Notes: Byte Order]
* [http://unixpapa.com/incnote/byteorder.html Unix Incompatibility Notes: Byte Order]
* [http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-291 The Art of ASSEMBLY LANGUAGE PROGRAMMING (The BSWAP Instruction)]
* [https://web.archive.org/web/20111016002758/http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-291 The Art of ASSEMBLY LANGUAGE PROGRAMMING (The BSWAP Instruction)]


{{Pahýl}}
{{Pahýl}}
{{Autoritní data}}


[[Kategorie:Počítače]]
[[Kategorie:Počítače]]
[[Kategorie:Kódování znaků]]
[[Kategorie:Kódování znaků]]

[[ca:Endianness]]
[[de:Byte-Reihenfolge]]
[[en:Endianness]]
[[es:Endianness]]
[[fi:Tavujärjestys]]
[[fr:Endianness]]
[[he:סדר בתים]]
[[hu:Byte-sorrend]]
[[it:Ordine dei byte]]
[[ja:エンディアン]]
[[ko:엔디언]]
[[lt:Baitų seka žodyje]]
[[nl:Endianness]]
[[pl:Kolejność bajtów]]
[[pt:Extremidade (ordenação)]]
[[ru:Порядок байтов]]
[[simple:Endianness]]
[[sk:Endianita]]
[[sr:Big endian]]
[[sv:Endian]]
[[tr:Endian]]
[[uk:Порядок байтів]]
[[zh:字节序]]

Aktuální verze z 19. 12. 2023, 21:15

Endianita (pořadí bajtů, anglicky byte order) je v informatice způsob uložení čísel v operační paměti počítače, který definuje, v jakém pořadí se uloží jednotlivé bajty číselného datového typu. Jde tedy o to, v jakém pořadí jsou v operační paměti uloženy jednotlivé řády čísel, které zabírají více než jeden bajt.

Endianita a kompatibilita[editovat | editovat zdroj]

Endianita je jedním ze základních zdrojů nekompatibility při ukládání a výměně dat v digitální podobě. Je nutné brát ji v úvahu při přenášení binárních souborů nebo při síťové komunikaci mezi různými platformami. Tento problém pramení z toho, že stejný zdrojový kód zkompilovaný pro počítače s různými procesory může kvůli jejich různé endianitě produkovat při ukládání nebo přenosu různá binární data. Nejrozšířenějším kódováním vícebajtových dat je v současnosti little endian, což je dané masovým rozšířením architektury Intel x86.

Zdrojem zmatků může být rovněž specifikace IEEE 754, která nedefinuje, v jakém pořadí bajtů se mají ukládat čísla v plovoucí řádové čárce. Endianita může způsobovat problémy i při práci s texty v kódování unicode, proto je rozumné tyto texty ukládat v kódování UTF-8, které je nezávislé na architektuře počítače.

Některé multiplatformní programy (např. OpenDocument nebo konkurenční Office Open XML) řeší problémy s hardwarovou endianitou tím, že ukládají data ve formě textů. I když problémy s kódováním se mohou vyskytnout i u textů, jsou mnohem snadněji řešitelné, protože nejsou dány hardwarem, ale pouze konvencí. Ve zmiňovaném případě OpenDocument a OOXML je tato konvence určena ISO normou.

Little-endian[editovat | editovat zdroj]

V tomto případě se na paměťové místo s nejnižší adresou uloží nejméně významný bajt (LSB) a za něj se ukládají ostatní bajty až po nejvíce významný bajt (MSB). Architektury uplatňující tento princip se nazývají little-endian (mnemotechnická pomůcka: little end first) a patří mezi ně MOS Technology 6502, Intel x86, Apple M1[1] a DEC VAX.

Little endian má jednu dobrou vlastnost. Jedna a ta samá hodnota může být z paměti načtena pro různou délku, bez změny adresy. Například 32bitový řetězec FF 00 00 00 může být načten ze stejné adresy jako 8bitový (hodnota = FF), 16bitový (00FF), 24bitový (0000FF), 32bitový (000000FF); jejich hodnota stále zůstává 255. Tato vlastnost je však velmi zřídka využívána programátory, kteří pracují s vyššími programovacími jazyky, proto se ponechává kompilátoru.

Např. 32bitové číslo 0x4A3B2C1D se na adresu 100 uloží takto:

100 101 102 103
... 1D 2C 3B 4A ...

Big-endian[editovat | editovat zdroj]

V tomto případě se na paměťové místo s nejnižší adresou uloží nejvíce významný bajt (MSB) a za něj se ukládají ostatní bajty až po nejméně významný bajt (LSB) na konci. Architektury uplatňující tento princip se nazývají big-endian (mnemotechnická pomůcka: big end first) a patří mezi ně Motorola 68000, SPARC a System/370.

Např. 32bitové číslo 0x4A3B2C1D se na adresu 100 uloží takto:

100 101 102 103
... 4A 3B 2C 1D ...

Middle-endian[editovat | editovat zdroj]

Některé architektury označované middle-endian (nebo někdy mixed-endian) užívají složitější způsob pro určení pořadí jednotlivých bajtů, který je dán kombinací obou výše zmíněných způsobů. Mezi takovéto architektury patří např. rodina procesorů PDP-11. Tento formát je také použit pro ukládání čísel s pohyblivou řádovou čárkou a dvojitou přesností v systémech VAX a ARM.

Např. 32bitové číslo 0x4A3B2C1D se na adresu 100 uloží takto:

100 101 102 103
... 3B 4A 1D 2C ...

nebo případně:

100 101 102 103
... 2C 1D 4A 3B ...

Endianity v souborech[editovat | editovat zdroj]

Pokud je binární soubor vytvořen a následně čten na počítačích, které mají různou endianitu, může vzniknout problém. Některé překladače mají vestavěná zařízení, která pracují s údaji zapsanými v jiných formátech. Například kompilátor Intel Fortran podporuje nestandardní CONVERT specifikátor, takže soubor lze otevřít jako:

OPEN (unit, CONVERT = 'BIG_ENDIAN',...)

nebo

OPEN (unit, CONVERT = 'LITTLE_ENDIAN',...)

Pokud kompilátor převod nepodporuje, musí záměnu bajtů (angl. byte swap) provést programátor. Neformátované sekvenční soubory v jazyce Fortran vytvořené s jednou endianitou obvykle není možné číst na systému pomocí jiné endianity. Fortran obvykle provádí záznam (napsán jediným příkazem Fortranu) jako data a pole. Ta jsou rovna celočíselným bytům v datech. Pokus o čtení těchto souborů v systému s jinými endianitami má pak za následek provozní chybu, protože pole počítače jsou nesprávná. Tomuto problému se lze vyhnout tím, že píšeme přímo do sekvenčního binárního souboru.

Aplikace formátující binární data, jako je například MATLAB .mat soubory, nebo formát dat BIL, používané v topografii, jsou obvykle nezávislé na endianitách.

Tohoto je dosaženo uložením dat vždy v jedné pevné endianitě nebo když spolu s údaji neseme příznak, který určí, s kterou endianitou byla data zapsána. Při čtení souboru převede aplikace endianity, je-li třeba. Tento postup je pro uživatele transparentní.

To je případ obrazových souborů TIFF, které informují ve svém záhlaví o endianitě použitých čísel. Pokud soubor začíná signaturou "MM", znamená to, že celá čísla jsou reprezentována jako velký endian, zatímco "II" endian malý. Každá z těchto signatur zabere jediné 16bitové slovo. Jsou typu palindrom (to znamená, že se čtou stejně dopředu i dozadu), takže jsou nezávislé endianitách. "I" znamená Intel a "M" znamená Motorola. Procesory Intel používají malý endian, zatímco procesory Motorola 680x0 velký endian. Tento explicitní podpis umožňuje čtecímu programu obrazových souborů TIFF výměnu bytů, a to pouze v případě, byl-li daný soubor vygenerován zapisovacím programem TIFF běžícím na počítači s jinou endianitou.

I když je programovací prostředí LabVIEW nejčastěji instalováno na počítačích s operačním systémem Windows, bylo nejprve vyvinuto pro Macintosh. Formát velkého endianu je používán pro binární čísla, zatímco většina Windows používá malý endian.

Za povšimnutí stojí fakt, že neexistuje obecný nástroj pro přeměnu endianit v souborech. Ke správnému převodu nutno znát strukturu souboru. Potřebná výměna bajtů závisí totiž na délce proměnných uložených v souboru (čtyřbajtové celé číslo vyžaduje jiný převod než dvojice dvoubajtových celých čísel).

Programování[editovat | editovat zdroj]

Konverze mezi little-endian a big-endian (jazyk C)[editovat | editovat zdroj]

Následující výpočty přehazují bajty z kódování little-endian na big-endian a naopak. Použití standardního C-jazyka vede k lepší přenositelnosti kódu. Zpracování pomocí strojových instrukcí je rychlejší, což může hrát roli při zpracování velkých souborů dat, nebo velkých datových toků. Syntaxe zápisu je provedena na způsob maker v jazyce C.

Aby se konverze provedla správně, musíme vědět jaký počet bajtů používá daný kompilátor C-jazyka pro určitý datový typ. Definice C-jazyka podle ANSI neurčuje, že např. typ int musí mít 32 bitů.

16bitový swap[editovat | editovat zdroj]

#define BSWAP16(n) ((n) << 8 | ((n) >> 8 & 0x00FF))

32bitový swap[editovat | editovat zdroj]

#define BSWAP32(n) (((n) & 0xFF000000L >> 24) | ((n) & 0x00FF0000L >> 8) | ((n) & 0x0000FF00L << 8) | ((n) & 0x000000FF << 24))

64bitový swap[editovat | editovat zdroj]

#define BSWAP64(n) ((n) >> 56) | (((n) << 40) & 0x00FF000000000000LL) | \
                                 (((n) << 24) & 0x0000FF0000000000LL) | \
                                 (((n) << 8)  & 0x000000FF00000000LL) | \
                                 (((n) >> 8)  & 0x00000000FF000000LL) | \
                                 (((n) >> 24) & 0x0000000000FF0000LL) | \
                                 (((n) >> 40) & 0x000000000000FF00LL) | \
                                 ((n) << 56)

Použití struktury union[editovat | editovat zdroj]

Následující kód demonstruje "přehazování" bajtů 32bitového datového typu. Obecně jde o poměrně neefektivní způsob konverze endianity, ale např. při smíšené endianitě by může být názornost tohoto postupu výhodou.

int32_t BSWAP32(int32_t data)
{
    int i, i2, tmp;
    union {
        int32_t val;
        uint8_t bytes[sizeof(int32_t)];
    } lf;
    lf.val = data;
    for(i = 0, i2 = sizeof(int32_t) -1; i < sizeof(int32_t) / 2; i++, i2--)
    {
        tmp = lf.bytes[i];
        lf.bytes[i] = lf.bytes[i2];
        lf.bytes[i2] = tmp;
    }
    return lf.val;
}

Architektura 80486 a odvozené[editovat | editovat zdroj]

Tato instrukce je k dispozici pouze na platformách Intel počínaje řadou 80486 (včetně) dál (486+) existuje pro konverzi endianity strojová instrukce bswap[2]. Tato instrukce slouží ke konverzi 32bitových nebo 64bitových hodnot z little-endian na big-endian a naopak. Zápis (použití) je následující:

BSWAP reg32
BSWAP reg64

Bere buď 32bitový nebo 64bitový registr. Pokud by se použil se 16bitovým registrem, zanechá jej beze změny a nic se neprovede. Pro 16bitový operand a změnu endianity lze na procesorech x86 provést instrukce XCHG, např.:

XCHG al, ah

Použití instrukce BSWAP v GCC[editovat | editovat zdroj]

Tuto instrukci je možno použít nepřímo voláním speciálního konstruktu překladače GCC (pro jazyk C/C++) od verze 4.3. Jedná se o vestavěnou (built-in) funkci, proto není potřeba vkládat žádný hlavičkový soubor, ani přilinkovat žádnou knihovnu (GCC místo nich vkládá přímo instrukce). Výhodou těchto funkcí je, že pokud daná platforma nemá instrukce pro bitovou konverzi mezi little-endian a big-endinan, vloží místo nich optimalizovaný kód.

Prototyp pro 32bitový swap[editovat | editovat zdroj]
int32_t __builtin_bswap32 (int32_t x);

Vrací 32bitovou hodnotu, která obsahuje přehozenou hodnotu 32bitové proměnné x (0x11223344 → 0x44332211).

Prototyp pro 64bitový swap[editovat | editovat zdroj]
int64_t __builtin_bswap64 (int64_t x);

Podobně jako __builtin_bswap32, ale pracuje s 64bitovými hodnotami (jak parametr, tak i návratová hodnota).

Pro 16bitovou konverzi neexistuje v GCC vestavěná (built-in) funkce.

Visual C++[editovat | editovat zdroj]

V jazyce C++ je potřeba vložit hlavičkový soubor intrin.h.

16bitový swap[editovat | editovat zdroj]

unsigned short _byteswap_ushort(unsigned short value);

32bitový swap[editovat | editovat zdroj]

unsigned long _byteswap_ulong(unsigned long value);

64bitový swap[editovat | editovat zdroj]

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

Detekce[editovat | editovat zdroj]

Detekci endianity je možné provést jak při překladu, tak při spuštění programu.

Při překladu (Unix)[editovat | editovat zdroj]

Detekce při překladu by měla být preferována, protože se provede pouze jednou a výsledný program je tak jako tak závislý na dané platformě, kde byl překompilován. Ve většině unixových systémů je k dispozici hlavičkový soubor sys/param.h, který kromě jiného poskytuje informace o pořadí bajtů (byte order). Definuje makra __BYTE_ORDER, __LITTLE_ENDIAN, __BIG_ENDIAN, a další, která lze využít např. následovně:

#include <sys/param.h>

#ifdef __BYTE_ORDER
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define ENDIAN_DEFAULT ENDIAN_LITTLE
# elif __BYTE_ORDER == __BIG_ENDIAN
# define ENDIAN_DEFAULT ENDIAN_BIG
# else
# error "Unknown byte order" /* Middle-endian? */
# endif
#endif /* __BYTE_ORDER */

Dalším možným přístupem je rozhodnutí na základě znalosti endianity příslušné platformy. To může být komplikované, protože se jména maker (symbolických konstant) mohou lišit v závislosti na použitém překladači a zároveň nemusí být možné podchytit všechny platformy (včetně hybridních architektur).

#if defined (i386) || defined (__i386__) || defined (__alpha) || defined (vax)
# define ENDIAN_DEFAULT ENDIAN_LITTLE
#else
# define ENDIAN_DEFAULT ENDIAN_BIG
#endif

Za běhu[editovat | editovat zdroj]

Pokud nejsou k dispozici žádná makra, je možné použít výpočet za běhu programu. Obě následující funkce vrací 1, když je platforma big-endian, jinak vrací 0.

int is_big_endian()
{
  static const int i = 1;
  return *((char *)&i) == 0;
}

Ve výše uvedeném kódu je do proměnné i (u které předpokládáme délku 32 bitů) uložena hodnota 1. V závislosti na endianitě dané platformy, se číslo uloží buď jako posloupnost bajtů {0x00,0x00,0x00,0x01} (big-endian) nebo jako posloupnost bajtů {0x01,0x00,0x00,x00} (little-endian). Funkce is_big_endian pouze kontroluje, jakou hodnotu obsahuje první bajt proměnné i.

Použití struktury union (Harbison and Steele)[editovat | editovat zdroj]

Příklad použití struktury union:[3]

int is_big_endian()
{
  union test_union
  {
    int i;
    char c;
  };

  static const union test_union s_t = { 1 };
  return s_t.c == 0;
}

Reference[editovat | editovat zdroj]

  1. Porting Your macOS Apps to Apple Silicon. Apple Developer Documentation [online]. [cit. 2023-12-19]. Dostupné online. (anglicky) 
  2. Intel Instruction Set (bswap)
  3. C: A Reference Manual. Samuel P. Harbison and Guy L. Steele, Jr. Third edition; Prentice-Hall, 1991, ISBN 0-13-110933-2

Externí odkazy[editovat | editovat zdroj]